1. Введение
Обзор
Функции Cloud Run – это предложение Google Cloud "Функция как услуга" на базе Cloud Run и Eventarc, которое обеспечивает более расширенный контроль над производительностью и масштабируемостью, а также над средой выполнения функций и триггерами из более чем 90 источников событий.
В этой практической работе вы узнаете, как создавать функции Cloud Run, которые реагируют на вызовы HTTP и запускаются сообщениями Pub/Sub и журналами Cloud Audit Logs.
В этой практической работе также используются автоматические обновления базового образа для развертывания функций путем указания базового образа с помощью флага --base-image. Автоматическое обновление базового образа для Cloud Run позволяет Google автоматически применять исправления системы безопасности к операционной системе и компонентам среды выполнения языка базового образа. Вам не нужно пересобирать или повторно развертывать сервис, чтобы обновить базовый образ. Дополнительную информацию можно найти в статье Автоматическое обновление базовых образов.
Если вы не хотите использовать автоматическое обновление базового образа, удалите флаг --base-image из примеров, приведенных в этой практической работе.
Что вы узнаете
- Обзор функций Cloud Run и использование автоматических обновлений базового образа.
- Как написать функцию, которая отвечает на HTTP-вызовы.
- Как написать функцию, которая отвечает на сообщения Pub/Sub.
- Как написать функцию, которая реагирует на события Cloud Storage.
- Как распределить трафик между двумя версиями.
- Как избавиться от холодных запусков с помощью минимального количества экземпляров.
2. Настройка и требования
Как создать корневую папку
Создайте корневую папку для всех примеров.
mkdir crf-codelab cd crf-codelab
Настройка переменных среды
Задайте переменные среды, которые будут использоваться в этой практической работе.
gcloud config set project <YOUR-PROJECT-ID> REGION=<YOUR_REGION> PROJECT_ID=$(gcloud config get-value project)
Как включить API
Включите все необходимые сервисы:
gcloud services enable \ artifactregistry.googleapis.com \ cloudbuild.googleapis.com \ eventarc.googleapis.com \ run.googleapis.com \ logging.googleapis.com \ pubsub.googleapis.com
3. Функция HTTP
Для первой функции давайте создадим аутентифицированную функцию Node.js, которая отвечает на HTTP-запросы. Также используем тайм-аут в 10 минут, чтобы показать, как функция может получить больше времени для ответа на HTTP-запросы.
Создать
Создайте папку для приложения и перейдите в нее:
mkdir hello-http cd hello-http
Создайте файл index.js, который отвечает на HTTP-запросы:
const functions = require('@google-cloud/functions-framework');
functions.http('helloWorld', (req, res) => {
res.status(200).send('HTTP with Node.js in Cloud Run functions!');
});
Создайте файл package.json, чтобы указать зависимости:
{
"name": "nodejs-run-functions-codelab",
"version": "0.0.1",
"main": "index.js",
"dependencies": {
"@google-cloud/functions-framework": "^2.0.0"
}
}
Развертывание
Разверните функцию:
gcloud run deploy nodejs-run-function \
--source . \
--function helloWorld \
--base-image nodejs22 \
--region $REGION \
--timeout 600 \
--no-allow-unauthenticated
Эта команда использует пакеты сборки, чтобы преобразовать исходный код функции в готовый к работе образ контейнера.
Обратите внимание на следующее:
- Флаг
--sourceуказывает Cloud Run, что функцию нужно встроить в сервис на основе контейнера, который можно запустить. - Флаг
--function(новый) используется, чтобы задать точку входа нового сервиса в качестве сигнатуры функции, которую вы хотите вызвать. - Флаг
--base-image(новый) указывает базовую среду образа для функции, напримерnodejs22,python312,go123,java21,dotnet8,ruby33илиphp83. Подробные сведения о базовых образах и пакетах, включенных в каждый образ, можно найти в статье Базовые образы сред выполнения. - (необязательно) Флаг
--timeoutпозволяет функции иметь более длительный тайм-аут для ответа на HTTP-запросы. В этом примере используется значение 600 секунд, что соответствует 10 минутам. - (необязательно)
--no-allow-unauthenticated, чтобы запретить публичный вызов функции.
Тестирование
Протестируйте функцию с помощью следующих команд:
# get the Service URL SERVICE_URL="$(gcloud run services describe nodejs-run-function --region $REGION --format 'value(status.url)')" # invoke the service curl -H "Authorization: bearer $(gcloud auth print-identity-token)" -X GET $SERVICE_URL
В ответе должно быть сообщение HTTP with Node.js in Cloud Run functions!.
4. Функция издатель-подписчик
Для второй функции давайте создадим функцию Python, запускаемую сообщением Pub/Sub, опубликованным в определенной теме.
Как настроить токены аутентификации издатель-подписчик
Если вы включили сервисный аккаунт Pub/Sub 8 апреля 2021 года или ранее, назначьте ему роль iam.serviceAccountTokenCreator:
PROJECT_NUMBER=$(gcloud projects list --filter="project_id:$PROJECT_ID" --format='value(project_number)') gcloud projects add-iam-policy-binding $PROJECT_ID \ --member serviceAccount:service-$PROJECT_NUMBER@gcp-sa-pubsub.iam.gserviceaccount.com \ --role roles/iam.serviceAccountTokenCreator
Создать
Создайте тему Pub/Sub, которая будет использоваться в примере:
TOPIC=cloud-run-functions-pubsub-topic gcloud pubsub topics create $TOPIC
Создайте папку для приложения и перейдите в нее:
mkdir ../hello-pubsub cd ../hello-pubsub
Создайте файл main.py, который регистрирует сообщение, содержащее идентификатор CloudEvent:
import functions_framework
@functions_framework.cloud_event
def hello_pubsub(cloud_event):
print('Pub/Sub with Python in Cloud Run functions! Id: ' + cloud_event['id'])
Создайте файл requirements.txt со следующим содержимым, чтобы указать зависимости:
functions-framework==3.*
Развертывание
Разверните функцию:
gcloud run deploy python-pubsub-function \
--source . \
--function hello_pubsub \
--base-image python313 \
--region $REGION \
--no-allow-unauthenticated
Получите номер проекта, который будет использоваться для идентификации сервисного аккаунта.
PROJECT_NUMBER=$(gcloud projects list --filter="project_id:$PROJECT_ID" --format='value(project_number)')
Создайте триггер
gcloud eventarc triggers create python-pubsub-function-trigger \
--location=$REGION \
--destination-run-service=python-pubsub-function \
--destination-run-region=$REGION \
--event-filters="type=google.cloud.pubsub.topic.v1.messagePublished" \
--transport-topic=projects/$PROJECT_ID/topics/$TOPIC \
--service-account=$PROJECT_NUMBER-compute@developer.gserviceaccount.com
Тестирование
Протестируйте функцию, отправив сообщение в тему:
gcloud pubsub topics publish $TOPIC --message="Hello World"
В журналах должно появиться полученное событие CloudEvent:
gcloud run services logs read python-pubsub-function --region $REGION --limit=10
5. Функция Cloud Storage
Теперь создадим функцию Node.js, которая будет реагировать на события из сегмента Cloud Storage.
Настроить
Чтобы использовать функции Cloud Storage, назначьте роль IAM pubsub.publisher сервисному аккаунту Cloud Storage:
SERVICE_ACCOUNT=$(gsutil kms serviceaccount -p $PROJECT_NUMBER) gcloud projects add-iam-policy-binding $PROJECT_ID \ --member serviceAccount:$SERVICE_ACCOUNT \ --role roles/pubsub.publisher
Создать
Создайте папку для приложения и перейдите в нее:
mkdir ../hello-storage cd ../hello-storage
Создайте файл index.js, который просто отвечает на события Cloud Storage:
const functions = require('@google-cloud/functions-framework');
functions.cloudEvent('helloStorage', (cloudevent) => {
console.log('Cloud Storage event with Node.js in Cloud Run functions!');
console.log(cloudevent);
});
Создайте файл package.json, чтобы указать зависимости:
{
"name": "nodejs-crf-cloud-storage",
"version": "0.0.1",
"main": "index.js",
"dependencies": {
"@google-cloud/functions-framework": "^2.0.0"
}
}
Развертывание
Сначала создайте сегмент Cloud Storage или используйте существующий.
export BUCKET_NAME="gcf-storage-$PROJECT_ID" export BUCKET="gs://gcf-storage-$PROJECT_ID" gsutil mb -l $REGION $BUCKET
Разверните функцию:
gcloud run deploy nodejs-crf-cloud-storage \ --source . \ --base-image nodejs22 \ --function helloStorage \ --region $REGION \ --no-allow-unauthenticated
После развертывания функции вы увидите ее в разделе Cloud Run в Cloud Console.
Теперь создайте триггер Eventarc.
BUCKET_REGION=$REGION gcloud eventarc triggers create nodejs-crf-cloud-storage-trigger \ --location=$BUCKET_REGION \ --destination-run-service=nodejs-crf-cloud-storage \ --destination-run-region=$REGION \ --event-filters="type=google.cloud.storage.object.v1.finalized" \ --event-filters="bucket=$BUCKET_NAME" \ --service-account=$PROJECT_NUMBER-compute@developer.gserviceaccount.com
Тестирование
Протестируйте функцию, загрузив файл в сегмент:
echo "Hello World" > random.txt gsutil cp random.txt $BUCKET/random.txt
В журналах должно появиться полученное событие CloudEvent:
gcloud run services logs read nodejs-crf-cloud-storage --region $REGION --limit=10
6. Журналы аудита Cloud
Теперь создадим функцию Node.js, которая получает событие журнала аудита Cloud при создании экземпляра виртуальной машины Compute Engine. В ответ на это он добавляет ярлык к новой ВМ, указывая ее создателя.
Как определить недавно созданные виртуальные машины Compute Engine
При создании виртуальной машины Compute Engine создает два журнала аудита.
Первое событие генерируется в начале создания ВМ. Второе – после создания виртуальной машины.
В журналах аудита поля операций различаются и содержат значения first: true и last: true. Второй журнал аудита содержит всю информацию, необходимую для маркировки экземпляра, поэтому мы будем использовать флаг last: true для его обнаружения в функциях Cloud Run.
Настроить
Чтобы использовать функции Cloud Audit Log, необходимо включить журналы аудита для Eventarc. Вам также потребуется сервисный аккаунт с ролью eventarc.eventReceiver.
- Включите журналы аудита Cloud Admin Read, Data Read и Data Write для Compute Engine API.
- Предоставьте сервисному аккаунту Compute Engine по умолчанию роль IAM
eventarc.eventReceiver:
gcloud projects add-iam-policy-binding $PROJECT_ID \ --member serviceAccount:$PROJECT_NUMBER-compute@developer.gserviceaccount.com \ --role roles/eventarc.eventReceiver
Создайте функцию
В этой практической работе используется Node.js, но вы можете найти другие примеры на странице https://github.com/GoogleCloudPlatform/eventarc-samples.
Создайте файл package.json
{
"dependencies": {
"googleapis": "^84.0.0"
}
}
Создайте файл node.js
// Copyright 2021 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
const { google } = require("googleapis");
var compute = google.compute("v1");
exports.labelVmCreation = async (cloudevent) => {
const data = cloudevent.body;
// in case an event has >1 audit log
// make sure we respond to the last event
if (!data.operation || !data.operation.last) {
console.log("Operation is not last, skipping event");
return;
}
// projects/dogfood-gcf-saraford/zones/us-central1-a/instances/instance-1
var resourceName = data.protoPayload.resourceName;
var resourceParts = resourceName.split("/");
var project = resourceParts[1];
var zone = resourceParts[3];
var instanceName = resourceParts[5];
var username = data.protoPayload.authenticationInfo.principalEmail.split("@")[0];
console.log(`Setting label username: ${username} to instance ${instanceName} for zone ${zone}`);
var authClient = await google.auth.getClient({
scopes: ["https://www.googleapis.com/auth/cloud-platform"]
});
// per docs: When updating or adding labels in the API,
// you need to provide the latest labels fingerprint with your request,
// to prevent any conflicts with other requests.
var labelFingerprint = await getInstanceLabelFingerprint(authClient, project, zone, instanceName);
var responseStatus = await setVmLabel(
authClient,
labelFingerprint,
username,
project,
zone,
instanceName
);
// log results of setting VM label
console.log(JSON.stringify(responseStatus, null, 2));
};
async function getInstanceLabelFingerprint(authClient, project, zone, instanceName) {
var request = {
project: project,
zone: zone,
instance: instanceName,
auth: authClient
};
var response = await compute.instances.get(request);
var labelFingerprint = response.data.labelFingerprint;
return labelFingerprint;
}
async function setVmLabel(authClient, labelFingerprint, username, project, zone, instanceName) {
var request = {
project: project,
zone: zone,
instance: instanceName,
resource: {
labels: { "creator": username },
labelFingerprint: labelFingerprint
},
auth: authClient
};
var response = await compute.instances.setLabels(request);
return response.statusText;
}
Развертывание
Разверните функцию:
gcloud run deploy gce-vm-labeler \ --source . \ --function labelVmCreation \ --region $REGION \ --no-allow-unauthenticated
Теперь создайте триггер. Обратите внимание, что функция фильтрует журналы аудита для вставок Compute Engine с флагом --trigger-event-filters.
gcloud eventarc triggers create gce-vm-labeler-trigger \ --location=$REGION \ --destination-run-service=gce-vm-labeler \ --destination-run-region=$REGION \ --event-filters="type=google.cloud.audit.log.v1.written,serviceName=compute.googleapis.com,methodName=v1.compute.instances.insert" \ --service-account=$ROJECT_NUMBER-compute@developer.gserviceaccount.com
Тестирование
Задайте переменные среды:
# if you're using europe-west1 as your region
ZONE=europe-west1-d
VM_NAME=codelab-crf-auditlog
Чтобы создать виртуальную машину, выполните следующую команду:
gcloud compute instances create $VM_NAME --zone=$ZONE --machine-type=e2-medium --image-family=debian-11 --image-project=debian-cloud
После создания виртуальной машины вы увидите добавленный ярлык creator в разделе Basic information (Основная информация) в Cloud Console или с помощью следующей команды:
gcloud compute instances describe $VM_NAME --zone=$ZONE
В выходных данных вы должны увидеть ярлык, как в следующем примере:
... labelFingerprint: ULU6pAy2C7s= labels: creator: atameldev ...
Организовать
Удалите экземпляр виртуальной машины. В этой практической работе он больше не понадобится.
gcloud compute instances delete $VM_NAME --zone=$ZONE
7. Разделение трафика
Функции Cloud Run поддерживают несколько версий функций, разделение трафика между разными версиями и откат функции к предыдущей версии.
На этом шаге вы развернете две версии функции, а затем разделите трафик между ними поровну.
Создать
Создайте папку для приложения и перейдите в нее:
mkdir ../traffic-splitting cd ../traffic-splitting
Создайте файл main.py с функцией Python, которая считывает переменную среды, отвечающую за цвет, и возвращает Hello World с этим цветом фона:
import os
color = os.environ.get('COLOR')
def hello_world(request):
return f'<body style="background-color:{color}"><h1>Hello World!</h1></body>'
Создайте файл requirements.txt со следующим содержимым, чтобы указать зависимости:
functions-framework==3.*
Развертывание
Разверните первую версию функции с оранжевым фоном:
COLOR=orange gcloud run deploy hello-world-colors \ --source . \ --base-image python313 \ --function hello_world \ --region $REGION \ --allow-unauthenticated \ --update-env-vars COLOR=$COLOR
Если вы протестируете функцию, открыв триггер HTTP (URI, полученный в результате выполнения команды развертывания) в браузере, то увидите Hello World на оранжевом фоне:

Разверните вторую версию с желтым фоном:
COLOR=yellow gcloud run deploy hello-world-colors \ --source . \ --base-image python313 \ --function hello_world \ --region $REGION \ --allow-unauthenticated \ --update-env-vars COLOR=$COLOR
Поскольку это последняя версия, при тестировании функции вы должны увидеть Hello World на желтом фоне:

Разделите трафик поровну
Чтобы разделить трафик между оранжевой и желтой версиями, вам нужно найти идентификаторы версий сервисов Cloud Run. Чтобы посмотреть идентификаторы версий, используйте следующую команду:
gcloud run revisions list --service hello-world-colors \ --region $REGION --format 'value(REVISION)'
Выходные данные должны быть похожи на эти:
hello-world-colors-00001-man hello-world-colors-00002-wok
Теперь разделите трафик между этими двумя версиями следующим образом (замените X-XXX на названия своих версий):
gcloud run services update-traffic hello-world-colors \ --region $REGION \ --to-revisions hello-world-colors-0000X-XXX=50,hello-world-colors-0000X-XXX=50
Тестирование
Протестируйте функцию, перейдя по ее общедоступному URL. В половине случаев вы должны увидеть оранжевую версию, а в другой половине – желтую:

Подробнее об откатах, постепенном развертывании и переносе трафика…
8. Минимум экземпляров
В функциях Cloud Run можно указать минимальное количество экземпляров функций, которые должны быть готовы к обработке запросов. Это позволяет ограничить количество холодных запусков.
На этом шаге вы развернете функцию с медленной инициализацией. Вы столкнетесь с проблемой холодного запуска. Затем вы развернете функцию с минимальным значением экземпляра, равным 1, чтобы избавиться от холодного запуска.
Создать
Создайте папку для приложения и перейдите в нее:
mkdir ../min-instances cd ../min-instances
Создайте файл main.go. В этом сервисе Go есть функция init, которая приостанавливает работу на 10 секунд, чтобы имитировать долгую инициализацию. В нем также есть функция HelloWorld, которая отвечает на HTTP-вызовы:
package p
import (
"fmt"
"net/http"
"time"
)
func init() {
time.Sleep(10 * time.Second)
}
func HelloWorld(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "Slow HTTP Go in Cloud Run functions!")
}
Развертывание
Разверните первую версию функции со значением по умолчанию для минимального количества экземпляров, равным нулю:
gcloud run deploy go-slow-function \ --source . \ --base-image go123 \ --function HelloWorld \ --region $REGION \ --no-allow-unauthenticated
Протестируйте функцию с помощью следующей команды:
# get the Service URL SERVICE_URL="$(gcloud run services describe go-slow-function --region $REGION --format 'value(status.url)')" # invoke the service curl -H "Authorization: bearer $(gcloud auth print-identity-token)" -X GET $SERVICE_URL
При первом вызове вы заметите задержку в 10 секунд (холодный запуск), а затем увидите сообщение. Последующие вызовы должны возвращаться немедленно.
Как задать минимальное количество экземпляров
Чтобы избежать холодного запуска при первом запросе, повторно разверните функцию с флагом --min-instances, установленным на 1, следующим образом:
gcloud run deploy go-slow-function \ --source . \ --base-image go123 \ --function HelloWorld \ --region $REGION \ --no-allow-unauthenticated \ --min-instances 1
Тестирование
Протестируйте функцию ещё раз:
curl -H "Authorization: bearer $(gcloud auth print-identity-token)" -X GET $SERVICE_URL
В первом запросе больше не должно быть 10-секундной задержки. Благодаря минимальному количеству экземпляров проблема холодного запуска при первом вызове (после долгого перерыва) исчезла.
Подробнее об использовании минимального количества экземпляров…
9. Поздравляем!
Поздравляем! Вы выполнили практическую работу.
Что мы обсудили
- Обзор функций Cloud Run и использование автоматических обновлений базового образа.
- Как написать функцию, которая отвечает на HTTP-вызовы.
- Как написать функцию, которая отвечает на сообщения Pub/Sub.
- Как написать функцию, которая реагирует на события Cloud Storage.
- Как распределить трафик между двумя версиями.
- Как избавиться от холодных запусков с помощью минимального количества экземпляров.