Разработка функций в Functions Framework и их развертывание в Yandex Serverless Containers
Functions Framework
Фреймворк Functions Framework позволяет создавать функции и запускать их в контейнерах Yandex Serverless Containers без использования сервиса Yandex Cloud Functions. Написанные этим способом функции можно переносить между различными платформами, такими как Cloud Run
В данном руководстве рассматривается сценарий, при котором вы локально создадите функцию с помощью фреймворка Functions Framework. Затем из этой функции вы соберете Docker-образ, который загрузите в реестр Yandex Container Registry. Из сохраненного в реестре Docker-образа вы создадите контейнер Serverless Containers, при вызове которого будет выполняться код вашей функции.
Предлагаемое решение позволяет:
- собирать и распространять функции как OCI-совместимые
Docker-образы, а также развертывать их на различных облачных и on-prem-платформах : Kubernetes , Cloud Run, Knative и др. - разрабатывать, локально запускать, отлаживать и тестировать функции как обычные веб-приложения с использованием современных IDE
; - переносить ваши функции из Cloud Run functions или Knative, сохранив совместимость с этими платформами;
- создавать функции на языках Dart
, C++ или Ruby , которые в настоящий момент не поддержаны в Cloud Functions.
В данном руководстве вы создадите тестовую функцию. Полный перечень поддерживаемых языков программирования см. в репозитории
Чтобы развернуть функцию в Serverless Containers:
- Подготовьте облако к работе.
- Создайте сервисный аккаунт.
- Создайте реестр Container Registry.
- Создайте функцию.
- Создайте Docker-образ и загрузите его в реестр Container Registry.
- Создайте контейнер Serverless Containers из загруженного Docker-образа.
- Проверьте работу функции в контейнере.
Если созданные ресурсы вам больше не нужны, удалите их.
Перед началом работы
Зарегистрируйтесь в Yandex Cloud и создайте платежный аккаунт:
- Перейдите в консоль управления
, затем войдите в Yandex Cloud или зарегистрируйтесь. - На странице Yandex Cloud Billing
убедитесь, что у вас подключен платежный аккаунт, и он находится в статусеACTIVE
илиTRIAL_ACTIVE
. Если платежного аккаунта нет, создайте его и привяжите к нему облако.
Если у вас есть активный платежный аккаунт, вы можете создать или выбрать каталог, в котором будет работать ваша инфраструктура, на странице облака
Подробнее об облаках и каталогах.
Необходимые платные ресурсы
В стоимость поддержки создаваемой инфраструктуры входят:
- плата за объем хранилища, объем исходящего трафика и использование сканера уязвимостей Container Registry (см. тарифы Yandex Container Registry);
- плата за использование сервиса Serverless Containers (см. тарифы Yandex Serverless Containers).
Настройте окружение
-
Установите утилиту cURL
. -
Установите и настройте интерфейс Yandex Cloud CLI.
-
Установите и настройте Docker.
-
Аутентифицируйтесь в Container Registry.
-
Установите утилиту Pack
. Для этого в терминале выполните команды и дождитесь завершения процесса установки:sudo add-apt-repository ppa:cncf-buildpacks/pack-cli -y sudo apt install pack-cli -y
Инструкция по установке утилиты Pack
приведена для ОС Linux Ubuntu. Если вы используете другую операционную систему, для установки воспользуйтесь документацией к утилите Pack.
Создайте сервисный аккаунт
- В консоли управления
выберите каталог, в котором вы будете создавать инфраструктуру. - В списке сервисов выберите Identity and Access Management.
- Нажмите кнопку Создать сервисный аккаунт и в открывшемся окне:
- Введите имя сервисного аккаунта:
serverless-containers-sa
. - Нажмите кнопку
Добавить роль и выберите рольcontainer-registry.images.puller
. - Нажмите кнопку Создать.
- Введите имя сервисного аккаунта:
По умолчанию используется каталог, указанный в профиле CLI. Вы можете указать другой каталог с помощью параметра --folder-name
или --folder-id
.
-
Создайте сервисный аккаунт
serverless-containers-sa
:yc iam service-account create \ --name serverless-containers-sa
Результат:
done (1s) id: aje7tnmd885t******** folder_id: b1gt6g8ht345******** created_at: "2025-02-14T11:09:54.376880905Z" name: serverless-containers-sa
Сохраните идентификаторы сервисного аккаунта (значение поля
id
) и каталога (значение поляfolder_id
) — они понадобятся на следующем шаге.Подробнее о команде
yc iam service-account create
читайте в справочнике CLI. -
Назначьте созданному сервисному аккаунту роль
container-registry.images.puller
на каталог, указав сохраненные на предыдущем шаге идентификаторы каталога и сервисного аккаунта:yc resource-manager folder add-access-binding <идентификатор_каталога> \ --role container-registry.images.puller \ --subject serviceAccount:<идентификатор_сервисного_аккаунта>
Результат:
done (2s) effective_deltas: - action: ADD access_binding: role_id: container-registry.images.puller subject: id: aje7tnmd885t******** type: serviceAccount
Подробнее о команде
yc resource-manager folder add-access-binding
читайте в справочнике CLI.
Чтобы создать сервисный аккаунт, воспользуйтесь методом REST API create для ресурса ServiceAccount или вызовом gRPC API ServiceAccountService/Create.
Чтобы назначить сервисному аккаунту роль на каталог, воспользуйтесь методом REST API updateAccessBindings для ресурса Folder или вызовом gRPC API FolderService/UpdateAccessBindings.
Создайте реестр Container Registry
- В консоли управления
выберите каталог, в котором вы ранее создали сервисный аккаунт. - В списке сервисов выберите Container Registry.
- Нажмите кнопку Создать реестр.
- В поле Имя задайте имя реестра:
functions-framework-registry
. - Нажмите кнопку Создать реестр.
- На открывшейся странице скопируйте идентификатор созданного реестра, он понадобится в дальнейшем.
Создайте реестр functions-framework-registry
:
yc container registry create \
--name functions-framework-registry
Результат:
done (1s)
id: crpfn9p374a3********
folder_id: b1gt6g8ht345********
name: functions-framework-registry
status: ACTIVE
created_at: "2025-02-14T11:44:23.698Z"
Сохраните идентификатор (значение поля id
) созданного реестра, он понадобится позднее.
Подробнее о команде yc container registry create
читайте в справочнике CLI.
Чтобы создать реестр, воспользуйтесь методом create для ресурса Registry или вызовом gRPC API RegistryService/Create.
Создайте функцию
На этом этапе вы локально создадите функцию с помощью фреймворка Functions Framework. Команды для установки пакетов приведены для ОС Linux Ubuntu. Если вы используете другую ОС, для установки воспользуйтесь документацией к вашей операционной системе.
Чтобы создать функцию на Node.js
-
Установите менеджер пакетов npm
:sudo apt update && \ sudo apt install npm -y
-
Создайте директорию для нового проекта:
mkdir my-first-function && \ cd my-first-function
-
Пройдите процедуру инициализации проекта:
npm init
В процессе инициализации оставьте все запрашиваемые значения по умолчанию, нажимая клавишу ENTER. В конце процесса в ответ на запрос
Is this OK?
введитеyes
и нажмите ENTER. В результате в директории с вашим проектом будет создан файл проектаpackage.json
:{ "name": "my-first-function", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "author": "", "license": "ISC" }
-
Создайте новый файл
index.js
, открыв его в текстовом редакторе:nano index.js
-
Добавьте в файл
index.js
следующий код:const functions = require('@google-cloud/functions-framework'); functions.http('helloWorld', (req, res) => { res.send('Hello, World!'); });
-
Установите фреймворк Functions Framework в ваш проект:
npm install @google-cloud/functions-framework
-
Добавьте в файл
package.json
следующий код:"scripts": { "start": "functions-framework --target=helloWorld" }
Пример итогового содержимого файла
package.json
:{ "name": "my-first-function", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "author": "", "license": "ISC", "dependencies": { "@google-cloud/functions-framework": "^3.4.5" }, "scripts": { "start": "functions-framework --target=helloWorld" } }
-
Локально запустите вашу функцию, чтобы убедиться, что все работает:
-
Запустите функцию:
npm start
Результат:
> my-first-function@1.0.0 start > functions-framework --target=helloWorld Serving function... Function: helloWorld Signature type: http URL: http://localhost:8080/
-
Откройте дополнительное окно терминала и в этом окне выполните команду:
curl http://localhost:8080/
Результат:
Hello, World!
Таким же способом вы можете запустить функцию в режиме отладки и использовать точки останова
с помощью современных IDE. -
Чтобы создать функцию на Python
-
Установите Python и менеджер пакетов pip
:sudo apt update && \ sudo apt install python3 python3-pip -y
-
Создайте директорию для нового проекта:
mkdir my-first-function && \ cd my-first-function
-
Установите фреймворк Functions Framework:
sudo pip install functions-framework
-
Создайте файл
main.py
с кодом функции, открыв его в текстовом редакторе:nano main.py
-
Добавьте в файл
main.py
следующий код:import flask import functions_framework @functions_framework.http def helloWorld(request: flask.Request) -> flask.typing.ResponseReturnValue: return "Hello, World!"
-
Локально запустите вашу функцию, чтобы убедиться, что все работает:
-
Запустите функцию:
functions-framework --target helloWorld --debug
Результат:
* Serving Flask app 'hello' * Debug mode: on WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead. * Running on all addresses (0.0.0.0) * Running on http://127.0.0.1:8080 * Running on http://192.168.1.15:8080 Press CTRL+C to quit * Restarting with watchdog (inotify) * Debugger is active! * Debugger PIN: 817-187-***
-
Откройте дополнительное окно терминала и в этом окне выполните команду:
curl http://localhost:8080/
Результат:
Hello, World!
Таким же способом вы можете запустить функцию в режиме отладки и использовать точки останова
с помощью современных IDE. -
Чтобы создать функцию на Go
-
Установите
Go версии1.18
или выше. -
Создайте директорию для нового проекта:
mkdir my-first-function && \ cd my-first-function
-
Создайте новый модуль:
go mod init example.com/helloWorld
-
Создайте файл
function.go
с кодом функции, открыв его в текстовом редакторе:nano function.go
-
Добавьте в файл
function.go
следующий код:package function import ( "fmt" "net/http" "github.com/GoogleCloudPlatform/functions-framework-go/functions" ) func init() { functions.HTTP("helloWorld", helloWorld) } // helloWorld writes "Hello, World!" to the HTTP response. func helloWorld(w http.ResponseWriter, r *http.Request) { fmt.Fprintln(w, "Hello, World!") }
-
Локально запустите вашу функцию, чтобы убедиться, что все работает:
-
Создайте директорию
cmd
:mkdir cmd
-
В директории
cmd
создайте файлmain.go
:nano cmd/main.go
-
Добавьте в файл
main.go
следующий код:package main import ( "log" "os" // Blank-import the function package so the init() runs _ "example.com/helloWorld" "github.com/GoogleCloudPlatform/functions-framework-go/funcframework" ) func main() { // Use PORT environment variable, or default to 8080. port := "8080" if envPort := os.Getenv("PORT"); envPort != "" { port = envPort } // By default, listen on all interfaces. If testing locally, run with // LOCAL_ONLY=true to avoid triggering firewall warnings and // exposing the server outside of your own machine. hostname := "" if localOnly := os.Getenv("LOCAL_ONLY"); localOnly == "true" { hostname = "127.0.0.1" } if err := funcframework.StartHostPort(hostname, port); err != nil { log.Fatalf("funcframework.StartHostPort: %v\n", err) } }
-
Обновите зависимости:
go mod tidy
-
Запустите локальный сервер:
FUNCTION_TARGET=helloWorld \ LOCAL_ONLY=true \ go run cmd/main.go
-
Откройте дополнительное окно терминала и в этом окне выполните команду:
curl http://localhost:8080/
Результат:
Hello, World!
Таким же способом вы можете запустить функцию в режиме отладки и использовать точки останова
с помощью современных IDE. -
Создайте Docker-образ и загрузите его в реестр Container Registry
Для сборки функции используется технология Buildpacks
-
Воспользуйтесь установленной ранее утилитой Pack, чтобы собрать Docker-образ с вашим приложением:
sudo pack build \ --builder gcr.io/buildpacks/builder:google-22 \ --env GOOGLE\_FUNCTION\_SIGNATURE\_TYPE=http \ --env GOOGLE\_FUNCTION\_TARGET=helloWorld \ my-first-function
Чтобы дополнительно настроить сборку контейнера, в т.ч. добавить в него дополнительные пакеты или зависимости, воспользуйтесь инструкцией
.Результат:
... Successfully built image my-first-function
Приведенная выше команда сборки может использоваться для функций, написанных на разных языках программирования, поскольку сборщик автоматически определяет язык и среду выполнения проекта, после чего собирает приложение в OCI-совместимый Docker-образ. Это избавляет разработчика от необходимости вручную создавать Dockerfile.
-
Локально запустите контейнер из собранного Docker-образа, чтобы убедиться, что все работает:
-
Выполните команду:
docker run --rm -p 8080:8080 my-first-function
-
Откройте дополнительное окно терминала и в этом окне выполните команду:
curl http://localhost:8080/
Результат:
Hello, World!
Код функции выполняется в запущенном локально Docker-контейнере.
-
Закройте дополнительное окно терминала. В основном окне терминала остановите выполнение Docker-контейнера, нажав сочетание клавиш Ctrl + C.
-
-
Присвойте созданному Docker-образу URL вида
cr.yandex/<идентификатор_реестра>/<имя_Docker-образа>:<тег>
, указав сохраненный ранее идентификатор реестра Container Registry:docker tag my-first-function \ cr.yandex/<идентификатор_реестра>/my-first-function:some-tag
Примечание
Загрузить в Container Registry можно только Docker-образы с URL вида
cr.yandex/<идентификатор_реестра>/<имя_Docker-образа>:<тег>
. -
Загрузите Docker-образ в реестр:
docker push \ cr.yandex/<идентификатор_реестра>/my-first-function:some-tag
Результат:
The push refers to repository [cr.yandex/crpfn9p374a3********/my-first-function] ... 14f9fd9947d2: Pushed 2573e0d81582: Pushed some-tag: digest: sha256:1b8bac8da5e64dd4359f81d71a7803f212af385f9718a7a4f9a40bca******** size: 2830
Создайте контейнер Serverless Containers из загруженного Docker-образа
Используйте загруженный в Container Registry Docker-образ, чтобы создать ревизию контейнера Serverless Containers.
-
В консоли управления
перейдите в каталог, в котором находятся созданные ранее ресурсы. -
В списке сервисов выберите Serverless Containers.
-
Нажмите кнопку Создать контейнер.
-
В поле Имя укажите имя контейнера:
my-first-function
. -
Нажмите кнопку Создать.
-
В блоке Параметры образа в поле URL образа выберите загруженный ранее Docker-образ
cr.yandex/<идентификатор_реестра>/my-first-function:some-tag
. -
В блоке Настройки в поле Сервисный аккаунт выберите созданный ранее сервисный аккаунт
serverless-containers-sa
. -
В блоке Логирование отключите опцию Запись логов, чтобы отключить запись логов в лог-группу.
Вы можете оставить опцию включенной, чтобы записывать логи в журнал выполнения контейнера. Запись и хранение логов тарифицируются.
-
Нажмите кнопку Создать ревизию.
-
В открывшемся окне в блоке Общая информация скопируйте значение поля Ссылка для вызова — этот URL понадобится при тестировании работы функции в контейнере.
-
Создайте контейнер
my-first-function
:yc serverless container create \ --name my-first-function
Результат:
done (1s) id: bba0tc5nv6j0******** folder_id: b1gt6g8ht345******** created_at: "2025-02-14T15:26:04.744Z" name: my-first-function url: https://bba0tc5nv6j0********.containers.yandexcloud.net/ status: ACTIVE
Сохраните полученную ссылку для вызова контейнера (значение поля
url
) — она понадобится при тестировании работы функции в контейнере.Подробнее о команде
yc serverless container create
читайте в справочнике CLI. -
Создайте ревизию созданного ранее контейнера:
yc serverless container revision deploy \ --container-name my-first-function \ --image "cr.yandex/<идентификатор_реестра>/my-first-function:some-tag" \ --service-account-id <идентификатор_сервисного_аккаунта> \ --no-logging
Где:
<идентификатор_реестра>
— сохраненный ранее идентификатор реестра Container Registry.<идентификатор_сервисного_аккаунта>
— сохраненный ранее идентификатор сервисного аккаунтаserverless-containers-sa
.--no-logging
— параметр отключает запись логов в лог-группу. Уберите этот параметр из команды, чтобы записывать логи в журнал выполнения контейнера. Запись и хранение логов тарифицируются.
Результат:
done (16s) id: bba6f1jllc3t******** container_id: bbakbil5lg7j******** created_at: "2025-02-14T20:48:06.424Z" image: image_url: cr.yandex/crpfn9p374a3********/my-first-function:some-tag image_digest: sha256:1b8bac8da5e64dd4359f81d71a7803f212af385f9718a7a4f9a40bca******** resources: memory: "134217728" cores: "1" core_fraction: "100" execution_timeout: 3s concurrency: "1" service_account_id: aje7tnmd885t******** status: ACTIVE log_options: disabled: true folder_id: b1gt6g8ht345******** runtime: http: {}
Подробнее о команде
yc serverless container revision deploy
читайте в справочнике CLI.
Чтобы создать контейнер, воспользуйтесь методом REST API create для ресурса Container или вызовом gRPC API ContainerService/Create.
Чтобы создать ревизию контейнера, воспользуйтесь методом REST API deployRevision для ресурса Container или вызовом gRPC API ContainerService/DeployRevision.
Проверьте работу функции в контейнере
После создания контейнера вы получили ссылку для его вызова. Чтобы проверить работу функции в контейнере, выполните запрос, указав в нем полученную ссылку:
curl \
--request GET \
--header "Authorization: Bearer $(yc iam create-token)" \
<ссылка_для_вызова_контейнера>
Результат:
Hello, World%
При вызове контейнера был выполнен код функции my-first-function
, созданной ранее с помощью фреймворка Functions Framework.
Как удалить созданные ресурсы
Чтобы перестать платить за созданные ресурсы: