Облачные функции HTTP в Python

1. Введение

b158ce75c3cccd6d.png

Python — популярный язык программирования с открытым исходным кодом, используемый учеными, разработчиками веб-приложений, системными администраторами и многими другими.

Cloud Functions — это управляемая событиями бессерверная вычислительная платформа. Облачные функции позволяют вам писать код, не беспокоясь о предоставлении ресурсов или масштабировании для удовлетворения меняющихся требований.

Существует два типа облачных функций:

  • Функции HTTP отвечают на HTTP-запросы. В этой лаборатории кода вы создадите пару.
  • Фоновые функции активируются такими событиями, как публикация сообщения в Cloud Pub/Sub или загрузка файла в Cloud Storage . Мы не рассматриваем этот вопрос в этой лабораторной работе, но вы можете прочитать больше в документации .

efb3268e3b74ed4f.png

Эта лаборатория кода поможет вам создать собственные облачные функции на Python.

Что ты построишь

В этой лаборатории кода вы опубликуете облачную функцию, которая при вызове через HTTP отображает логотип Python Powered :

a7aaf656b78050fd.png

Что вы узнаете

  • Как написать облачную функцию HTTP.
  • Как написать облачную функцию HTTP, которая принимает аргументы.
  • Как протестировать облачную функцию HTTP.
  • Как запустить локальный HTTP-сервер Python, чтобы опробовать эту функцию.
  • Как написать облачную функцию HTTP, которая возвращает изображение.

2. Настройка и требования

Самостоятельная настройка среды

  1. Войдите в Google Cloud Console и создайте новый проект или повторно используйте существующий. Если у вас еще нет учетной записи Gmail или Google Workspace, вам необходимо ее создать .

fbef9caa1602edd0.png

a99b7ace416376c4.png

5e3ff691252acf41.png

  • Имя проекта — это отображаемое имя для участников этого проекта. Это строка символов, не используемая API Google. Вы всегда можете обновить его.
  • Идентификатор проекта уникален для всех проектов Google Cloud и является неизменяемым (невозможно изменить после его установки). Cloud Console автоматически генерирует уникальную строку; обычно тебя не волнует, что это такое. В большинстве лабораторий кода вам потребуется указать идентификатор проекта (обычно идентифицируемый как PROJECT_ID ). Если вам не нравится сгенерированный идентификатор, вы можете создать другой случайный идентификатор. Альтернативно, вы можете попробовать свой собственный и посмотреть, доступен ли он. Его нельзя изменить после этого шага и он сохраняется на протяжении всего проекта.
  • К вашему сведению, есть третье значение — номер проекта , которое используют некоторые API. Подробнее обо всех трех этих значениях читайте в документации .
  1. Затем вам необходимо включить выставление счетов в Cloud Console, чтобы использовать облачные ресурсы/API. Прохождение этой кодовой лаборатории не будет стоить много, если вообще что-то стоить. Чтобы отключить ресурсы и избежать выставления счетов за пределами этого руководства, вы можете удалить созданные вами ресурсы или удалить проект. Новые пользователи Google Cloud имеют право на участие в программе бесплатной пробной версии стоимостью 300 долларов США .

Запустить Cloud Shell

Хотя Google Cloud можно управлять удаленно с вашего ноутбука, в этой лаборатории вы будете использовать Cloud Shell , среду командной строки, работающую в облаке.

Активировать Cloud Shell

  1. В Cloud Console нажмите «Активировать Cloud Shell». 853e55310c205094.png .

3c1dabeca90e44e5.png

Если вы запускаете Cloud Shell впервые, вы увидите промежуточный экран с описанием того, что это такое. Если вам был представлен промежуточный экран, нажмите «Продолжить» .

9c92662c6a846a5c.png

Подготовка и подключение к Cloud Shell займет всего несколько минут.

9f0e51b578fecce5.png

Эта виртуальная машина загружена всеми необходимыми инструментами разработки. Он предлагает постоянный домашний каталог объемом 5 ГБ и работает в Google Cloud, что значительно повышает производительность сети и аутентификацию. Большую часть, если не всю, работу в этой лаборатории кода можно выполнить с помощью браузера.

После подключения к Cloud Shell вы увидите, что вы прошли аутентификацию и что для проекта установлен идентификатор вашего проекта.

  1. Выполните следующую команду в Cloud Shell, чтобы подтвердить, что вы прошли аутентификацию:
gcloud auth list

Вывод команды

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

To set the active account, run:
    $ gcloud config set account `ACCOUNT`
  1. Выполните следующую команду в Cloud Shell, чтобы убедиться, что команда gcloud знает о вашем проекте:
gcloud config list project

Вывод команды

[core]
project = <PROJECT_ID>

Если это не так, вы можете установить это с помощью этой команды:

gcloud config set project <PROJECT_ID>

Вывод команды

Updated property [core/project].

Убедитесь, что облачные функции и API-интерфейсы Cloud Build включены.

Выполните следующую команду из Cloud Shell, чтобы убедиться, что облачные функции и API Cloud Build включены:

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

Примечание. Cloud Build будет вызываться командой gcloud functions deploy и автоматически встроит ваш код в образ контейнера.

Загрузите исходный код

В терминале Cloud Shell выполните следующие команды:

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

Проверьте содержимое исходного каталога:

ls

У вас должны быть следующие файлы:

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

3. Знакомство с облачными функциями HTTP

Облачные функции HTTP в Python написаны как обычные функции Python. Функция должна принимать один аргумент flask.Request , который обычно называется 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")

# ...

Вы можете открыть файл с помощью предпочитаемого вами редактора командной строки (nano, vim или emacs). Вы также можете открыть его в редакторе Cloud Shell, установив исходный каталог в качестве рабочей области:

cloudshell open-workspace .

Давайте развернем эту функцию как облачную функцию HTTP, используя команду gcloud functions deploy :

FUNCTION_NAME="hello_world"

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

Вывод команды:

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

Примечания о параметрах gcloud functions deploy :

  • --runtime : указывает среду выполнения языка. Для Python в настоящее время это может быть python37 , python38 , python39 , python310 или python312 . См. Среды выполнения .
  • --trigger-http : функции будет назначена конечная точка. HTTP-запросы (POST, PUT, GET, DELETE и OPTIONS) к конечной точке инициируют выполнение функции.
  • --allow-unauthenticated : функция будет общедоступной, разрешая всем вызывающим абонентам без проверки аутентификации.
  • Дополнительную информацию см. в разделе развертывание функций gcloud .

Чтобы протестировать функцию, вы можете щелкнуть URL-адрес httpsTrigger.url , отображаемый в выводе команды выше. Вы также можете программно получить URL-адрес и вызвать функцию с помощью следующих команд:

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

Вы должны получить следующий результат:

Hello World! 👋

4. Написание облачной функции HTTP, принимающей аргументы

Функции становятся более универсальными, когда они принимают аргументы. Давайте определим новую функцию hello_name , которая поддерживает параметр 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")

# ...

Давайте развернем эту новую функцию:

FUNCTION_NAME="hello_name"

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

Вывод команды:

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

Чтобы протестировать функцию, вы можете щелкнуть URL-адрес httpsTrigger.url , отображаемый в выводе команды выше. Вы также можете программно получить URL-адрес и вызвать функцию с помощью следующих команд:

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

Вы должны получить результат по умолчанию:

Hello World! 🚀

Вы получаете результат по умолчанию, поскольку аргумент name не установлен. Добавьте параметр в URL:

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

На этот раз вы получите свой индивидуальный ответ:

Hello YOUR NAME! 🚀

Следующий шаг — добавить модульные тесты, чтобы убедиться, что ваши функции продолжают работать должным образом при обновлении исходного кода.

5. Написание тестов

Облачные функции HTTP в Python тестируются с использованием модуля unittest из стандартной библиотеки. Для проверки вашей функции не требуется запускать эмулятор или другое моделирование — достаточно обычного кода Python.

Вот как выглядит тест для функций hello_world и 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. Тесты Python пишутся так же, как и другие файлы Python. Они начинаются с набора импорта, затем определяют классы и функции.
  2. Объявление теста имеет форму class TestHello(TestCase) . Это должен быть класс, наследуемый от unittest.TestCase .
  3. В тестовом классе есть методы, каждый из которых должен начинаться с test_ , которые представляют отдельные тестовые случаи.
  4. Каждый тестовый пример тестирует одну из наших функций, имитируя параметр request (т. е. заменяя его поддельным объектом с конкретными данными, необходимыми для теста).
  5. После вызова каждой функции тест проверяет ответ HTTP, чтобы убедиться, что он соответствует нашим ожиданиям.

Поскольку main.py зависит от flask , убедитесь, что в вашей тестовой среде установлена ​​платформа Flask:

pip install flask

Установка Flask выдает результат, аналогичный следующему:

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

Запустите эти тесты локально:

python -m unittest

Три модульных теста должны пройти:

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

OK

Далее вы создадите новую функцию, которая возвращает логотип Python Powered.

6. Написание облачной функции HTTP на базе Python

Давайте сделаем новую функцию немного более интересной, возвращая изображение «Python Powered» для каждого запроса:

a7aaf656b78050fd.png

В следующем листинге показан код, позволяющий это реализовать:

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

Разверните новую функцию python_powered :

FUNCTION_NAME="python_powered"

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

Вывод команды:

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

Чтобы протестировать функцию, щелкните URL-адрес httpsTrigger.url , отображаемый в выводе команды выше. Если все работает правильно, вы увидите логотип «Python Powered» в новой вкладке браузера!

Далее вы создадите приложение, чтобы можно было запустить и опробовать свою функцию локально перед развертыванием.

7. Запуск функции локально

Вы можете запустить функцию HTTP локально, создав веб-приложение и вызвав функцию в маршруте. Вы можете добавить его в тот же каталог, что и ваша функция. Файл с именем web_app.py имеет следующее содержимое:

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. Этот файл создает приложение Flask.
  2. Он регистрирует маршрут по базовому URL-адресу, который обрабатывается функцией index() .
  3. Затем функция index() вызывает нашу функцию python_powered , передавая ей текущий запрос.

Убедитесь, что платформа Flask установлена ​​в вашей среде разработки:

pip install flask

Установка Flask выдает результат, аналогичный следующему:

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

Чтобы запустить это приложение локально, выполните следующую команду:

python web_app.py

Теперь используйте Cloud Shell Web Preview, чтобы протестировать веб-приложение в браузере. В Cloud Shell нажмите кнопку «Просмотр в Интернете» и выберите «Просмотр на порту 8080»:

6c9ff9e5c692c58e.gif

Cloud Shell открывает URL-адрес предварительного просмотра на своем прокси-сервисе в новом окне браузера. Веб-предварительный просмотр ограничивает доступ по HTTPS только к вашей учетной записи пользователя. Если все работает правильно, вы должны увидеть логотип Python Powered!

8e5c3ead11cfd103.png

8. Поздравляем!

b158ce75c3cccd6d.png

Вы развернули облачные функции HTTP, используя идиоматические функции, которые обрабатывают веб-запросы с помощью платформы Flask.

Цены на облачные функции зависят от того, как часто вызывается ваша функция, включая уровень бесплатного пользования для функций, которые запускаются нечасто. Закончив тестирование своих облачных функций, вы можете удалить их с помощью gcloud :

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

Вы также можете удалить функции из консоли Google Cloud .

Мы надеемся, что вам понравится использование облачных функций в Python!