Начало работы с функциями Cloud Run

1. Введение

Обзор

Функции Cloud Run — это предложение «функции как услуга» от Google Cloud, работающее на базе Cloud Run и Eventarc , которое обеспечивает более расширенный контроль над производительностью и масштабируемостью, а также больший контроль над средой выполнения функций и триггерами из более чем 90 источников событий.

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

В этой лабораторной работе также используются автоматические обновления базовых образов для развёртывания функций. Для этого используется флаг --base-image указывающий базовый образ. Автоматическое обновление базовых образов для Cloud Run позволяет Google автоматически устанавливать исправления безопасности для операционной системы и компонентов среды выполнения языка базового образа. Вам не придётся пересобирать или повторно развертывать сервис для обновления базового образа. Подробнее см. в разделе «Автоматические обновления базовых образов».

Если вы предпочитаете не использовать автоматическое обновление базового образа, вы можете удалить флаг --base-image из примеров, показанных в этой кодовой лаборатории.

Чему вы научитесь

  • Обзор функций Cloud Run и как использовать автоматическое обновление базового образа.
  • Как написать функцию, которая реагирует на HTTP-вызовы.
  • Как написать функцию, которая реагирует на сообщения Pub/Sub.
  • Как написать функцию, реагирующую на события облачного хранилища.
  • Как разделить трафик между двумя ревизиями.
  • Как избавиться от холодного запуска двигателя с минимальными затратами времени.

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

Эта команда использует buildpack-пакеты для преобразования исходного кода вашей функции в готовый к использованию образ контейнера.

Обратите внимание на следующее:

  • Флаг --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

Если вы включили учетную запись службы Pub/Sub 8 апреля 2021 г. или ранее, предоставьте роль iam.serviceAccountTokenCreator учетной записи службы Pub/Sub:

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. Функция облачного хранения

Для следующей функции давайте создадим функцию Node.js, которая реагирует на события из контейнера облачного хранилища.

Настраивать

Чтобы использовать функции 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 , который просто реагирует на события облачного хранилища:

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

Развертывать

Сначала создайте контейнер облачного хранилища (или используйте уже имеющийся у вас контейнер):

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. Журналы облачного аудита

Для следующей функции создадим функцию Node.js, которая получает событие Cloud Audit Log при создании экземпляра виртуальной машины Compute Engine. В ответ она добавляет метку к только что созданной виртуальной машине, указывающую её создателя.

Определить вновь созданные виртуальные машины Compute Engine

Compute Engine создает 2 журнала аудита при создании виртуальной машины.

Первый генерируется в начале создания виртуальной машины. Второй — после создания виртуальной машины.

В журналах аудита поля операций различаются и содержат значения first: true и last: true . Второй журнал аудита содержит всю информацию, необходимую для маркировки экземпляра, поэтому мы будем использовать флаг last: true для его обнаружения в функциях Cloud Run.

Настраивать

Для использования функций журнала аудита облака необходимо включить журналы аудита для Eventarc. Также необходимо использовать учетную запись службы с ролью eventarc.eventReceiver .

  1. Включите типы журналов облачного аудита: Admin Read , Data Read и Data Write для API Compute Engine.
  2. Предоставьте учетной записи службы 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 на виртуальной машине в 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 поддерживают множественные версии ваших функций, разделяя трафик между различными версиями и откатывая вашу функцию к предыдущей версии.

На этом этапе вы развернете 2 версии функции, а затем разделите трафик между ними в соотношении 50/50.

Создавать

Создайте папку для приложения и перейдите в нее:

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 на оранжевом фоне:

36ca0c5f39cc89cf.png

Разверните вторую ревизию с желтым фоном:

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 на желтом фоне:

391286a08ad3cdde.png

Разделить трафик 50/50

Чтобы разделить трафик между оранжевой и жёлтой ревизиями, необходимо найти идентификаторы ревизий сервисов 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-адресу. В половине случаев вы должны видеть оранжевую версию, а в другой половине — жёлтую.

36ca0c5f39cc89cf.png391286a08ad3cdde.png

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

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.
  • Как написать функцию, реагирующую на события облачного хранилища.
  • Как разделить трафик между двумя ревизиями.
  • Как избавиться от холодного запуска двигателя с минимальными затратами времени.