1. مقدمه
نمای کلی
توابع Cloud Run یک سرویس Google Cloud Functions-as-a-Service است که توسط Cloud Run و Eventarc ارائه میشود و به شما کنترل پیشرفتهتری بر عملکرد و مقیاسپذیری میدهد و کنترل بیشتری در مورد زمان اجرا و راهاندازهای عملکردها از بیش از ۹۰ منبع رویداد به شما میدهد.
این لبه کد شما را در ایجاد عملکردهای Cloud Run که به تماسهای HTTP پاسخ میدهند و توسط پیامهای Pub/Sub و گزارشهای Cloud Audit فعال میشوند، راهنمایی میکند.
این کد لبه همچنین از به روز رسانی خودکار تصویر پایه برای استقرار عملکرد با تعیین یک تصویر پایه با استفاده از پرچم --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
این دستور از buildpack ها برای تبدیل کد منبع تابع شما به یک تصویر ظرف آماده برای تولید استفاده می کند.
لطفا به موارد زیر توجه کنید:
- پرچم
--sourceبرای گفتن به Cloud Run استفاده می شود تا تابع را در یک سرویس مبتنی بر کانتینر قابل اجرا بسازد. - پرچم
--function(جدید) برای تنظیم نقطه ورودی سرویس جدید به عنوان امضای تابعی که می خواهید فراخوانی شود استفاده می شود. - پرچم
--base-image(جدید) محیط تصویر پایه را برای عملکرد شما مشخص می کند، مانندnodejs22،python312،go123،java21،dotnet8،ruby33، یاphp83. برای جزئیات بیشتر در مورد تصاویر پایه و بسته های موجود در هر تصویر، به تصاویر پایه Runtimes مراجعه کنید. - (اختیاری) پرچم
--timeoutبه تابع اجازه می دهد تا زمان بیشتری برای پاسخ به درخواست های HTTP داشته باشد. در این مثال، 600 ثانیه برای نشان دادن زمان پاسخ دهی دقیقه ای استفاده می شود. - (اختیاری)
--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. میخانه / تابع فرعی
برای تابع دوم، بیایید یک تابع پایتون ایجاد کنیم که توسط یک پیام Pub/Sub منتشر شده در یک موضوع خاص راه اندازی می شود.
توکنهای Pub/Sub auth را تنظیم کنید
اگر حساب سرویس 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، نقش pubsub.publisher IAM را به حساب سرویس 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. گزارش های حسابرسی ابری
برای تابع بعدی، اجازه دهید یک تابع Node.js ایجاد کنیم که یک رویداد Cloud Audit Log را هنگام ایجاد نمونه Compute Engine VM دریافت می کند. در پاسخ، یک برچسب به VM تازه ایجاد شده اضافه می کند که سازنده ماشین مجازی را مشخص می کند.
VMهای موتور محاسباتی جدید ایجاد شده را تعیین کنید
موتور محاسباتی 2 گزارش حسابرسی را هنگام ایجاد یک VM منتشر می کند.
اولین مورد در ابتدای ایجاد VM منتشر می شود. مورد دوم پس از ایجاد VM منتشر می شود.
در گزارشهای حسابرسی، فیلدهای عملیات متفاوت هستند، که شامل first: true و last: true هستند. دومین گزارش حسابرسی شامل تمام اطلاعاتی است که برای برچسب گذاری یک نمونه نیاز داریم، بنابراین last: true برای شناسایی آن در توابع اجرای ابری استفاده می کنیم.
راه اندازی کنید
برای استفاده از توابع Cloud Audit Log، باید Audit Logs را برای Eventarc فعال کنید. همچنین باید از یک حساب کاربری با نقش eventarc.eventReceiver استفاده کنید.
- انواع گزارشهای گزارش حسابرسی Cloud Audit Admin Read ، Data Read و Data Write را برای Compute Engine API فعال کنید.
- به حساب پیش فرض سرویس Compute Engine نقش
eventarc.eventReceiverIAM اعطا کنید:
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
حالا ماشه را ایجاد کنید. به نحوه فیلتر کردن عملکرد در گزارش های حسابرسی برای درج موتور محاسباتی با پرچم --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
تست کنید
env vars را تنظیم کنید:
# if you're using europe-west1 as your region
ZONE=europe-west1-d
VM_NAME=codelab-crf-auditlog
برای ایجاد یک VM دستور زیر را اجرا کنید:
gcloud compute instances create $VM_NAME --zone=$ZONE --machine-type=e2-medium --image-family=debian-11 --image-project=debian-cloud
پس از تکمیل ساخت VM، باید برچسب creator اضافه شده را در VM در کنسول Cloud در بخش اطلاعات پایه یا با استفاده از دستور زیر مشاهده کنید:
gcloud compute instances describe $VM_NAME --zone=$ZONE
شما باید برچسب را در خروجی مانند مثال زیر ببینید:
... labelFingerprint: ULU6pAy2C7s= labels: creator: atameldev ...
پاک کن
حتماً نمونه VM را حذف کنید. دیگر در این آزمایشگاه استفاده نخواهد شد.
gcloud compute instances delete $VM_NAME --zone=$ZONE
7. تقسیم ترافیک
توابع Cloud Run از ویرایش های متعدد توابع شما پشتیبانی می کند، ترافیک را بین نسخه های مختلف تقسیم می کند و عملکرد شما را به نسخه قبلی برمی گرداند.
در این مرحله، 2 ویرایش از یک تابع را اجرا می کنید و سپس ترافیک را بین آنها 50-50 تقسیم می کنید.
ایجاد کنید
یک پوشه برای برنامه ایجاد کنید و به پوشه بروید:
mkdir ../traffic-splitting cd ../traffic-splitting
یک فایل main.py با یک تابع پایتون ایجاد کنید که متغیر محیط رنگی را می خواند و با 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 با پس زمینه زرد ببینید:

ترافیک را 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 عمومی آن تست کنید. نیمی از مواقع باید نسخه نارنجی و نیمی دیگر نسخه زرد را ببینید:


برای اطلاعات بیشتر به بازگشت، عرضه تدریجی و انتقال ترافیک مراجعه کنید.
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 پاسخ می دهد.
- نحوه تقسیم ترافیک بین دو نسخه
- نحوه خلاص شدن از شر سرما با حداقل موارد شروع می شود.