بدء استخدام دوال 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
  • كيفية كتابة دالة تستجيب لأحداث 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)

تفعيل واجهات برمجة التطبيقات

فعِّل جميع الخدمات اللازمة:

gcloud services enable \
  artifactregistry.googleapis.com \
  cloudbuild.googleapis.com \
  eventarc.googleapis.com \
  run.googleapis.com \
  logging.googleapis.com \
  pubsub.googleapis.com

3- دالة HTTP

بالنسبة إلى الدالة الأولى، لننشئ دالة authenticated 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. دالة Pub/Sub

بالنسبة إلى الدالة الثانية، لننشئ دالة Python يتم تشغيلها من خلال رسالة Pub/Sub يتم نشرها في موضوع معيّن.

إعداد رموز المصادقة في Pub/Sub

إذا فعّلت حساب خدمة Pub/Sub في 8 أبريل 2021 أو قبل ذلك، امنح حساب خدمة Pub/Sub الدور 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، امنح دور 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 Audit Logs

بالنسبة إلى الدالة التالية، لننشئ دالة Node.js تتلقّى حدث Cloud Audit Log عند إنشاء مثيل جهاز افتراضي في Compute Engine. ورداً على ذلك، يضيف تصنيفًا إلى الجهاز الظاهري الذي تم إنشاؤه حديثًا، ويحدّد منشئ الجهاز الظاهري.

تحديد الأجهزة الافتراضية التي تم إنشاؤها حديثًا في Compute Engine

يُصدر Compute Engine سجلَّي تدقيق عند إنشاء جهاز افتراضي.

يتم إرسال الحدث الأول في بداية عملية إنشاء الجهاز الافتراضي. يتم إصدار الحدث الثاني بعد إنشاء الجهاز الظاهري.

في سجلّات التدقيق، تختلف حقول العمليات، إذ تحتوي على القيم first: true وlast: true. يحتوي سجلّ التدقيق الثاني على جميع المعلومات التي نحتاج إليها لتصنيف مثيل، لذلك سنستخدم العلامة last: true لرصده في دوال Cloud Run.

إعداد

لاستخدام وظائف Cloud Audit Log، يجب تفعيل سجلّات التدقيق في Eventarc. يجب أيضًا استخدام حساب خدمة لديه دور eventarc.eventReceiver.

  1. فعِّل سجلّات Cloud Audit Logs لأنواع السجلّات قراءة المشرف وقراءة البيانات وكتابة البيانات لواجهة Compute Engine API.
  2. امنح حساب الخدمة التلقائي في Compute Engine دور 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 مراجعات متعددة لدوالك، وتقسيم عدد الزيارات بين المراجعات المختلفة، وإرجاع دالتك إلى إصدار سابق.

في هذه الخطوة، ستنشر إصدارَين من إحدى الدوال ثم تقسم عدد الزيارات بينهما بالتساوي.

الإنشاء

أنشِئ مجلدًا للتطبيق وانتقِل إليه:

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 (ناتج معرّف الموارد المنتظم الخاص بأمر النشر أعلاه) في المتصفّح، من المفترض أن يظهر لك 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.png 391286a08ad3cdde.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
  • كيفية كتابة دالة تستجيب لأحداث Cloud Storage
  • كيفية تقسيم عدد الزيارات بين إصدارَين
  • كيفية التخلّص من عمليات التشغيل على البارد باستخدام الحد الأدنى من المثيلات