الصورة اليومية: التمرين المعملي 3: إنشاء صورة مجمّعة لأحدث الصور

1. نظرة عامة

في هذا الدرس العملي، ستنشئ خدمة Cloud Run جديدة، وهي خدمة تجميع الصور، وسيتم تشغيلها من خلال Cloud Scheduler على فترات زمنية منتظمة. تسترجع الخدمة أحدث الصور التي تم تحميلها وتنشئ صورة مجمّعة من هذه الصور: فهي تعثر على قائمة بالصور الحديثة في Cloud Firestore، ثم تنزّل ملفات الصور الفعلية من Cloud Storage.

df20f5d0402b54b4.png

أهداف الدورة التعليمية

  • Cloud Run
  • Cloud Scheduler
  • Cloud Storage
  • Cloud Firestore

2. الإعداد والمتطلبات

إعداد البيئة بالسرعة التي تناسبك

  1. سجِّل الدخول إلى Google Cloud Console وأنشِئ مشروعًا جديدًا أو أعِد استخدام مشروع حالي. إذا لم يكن لديك حساب على Gmail أو Google Workspace، عليك إنشاء حساب.

96a9c957bc475304.png

b9a10ebdf5b5a448.png

a1e3c01a38fa61c2.png

  • اسم المشروع هو الاسم المعروض للمشاركين في هذا المشروع. وهي سلسلة من الأحرف لا تستخدمها Google APIs، ويمكنك تعديلها في أي وقت.
  • يجب أن يكون رقم تعريف المشروع فريدًا في جميع مشاريع Google Cloud، كما أنّه غير قابل للتغيير (لا يمكن تغييره بعد ضبطه). تنشئ Cloud Console تلقائيًا سلسلة فريدة، ولا يهمّك عادةً ما هي. في معظم دروس الترميز، عليك الرجوع إلى رقم تعريف المشروع (ويتم تحديده عادةً على أنّه PROJECT_ID)، لذا إذا لم يعجبك، يمكنك إنشاء رقم آخر عشوائي، أو يمكنك تجربة رقمك الخاص ومعرفة ما إذا كان متاحًا. ثم يتم "تجميده" بعد إنشاء المشروع.
  • هناك قيمة ثالثة، وهي رقم المشروع الذي تستخدمه بعض واجهات برمجة التطبيقات. يمكنك الاطّلاع على مزيد من المعلومات عن كل هذه القيم الثلاث في المستندات.
  1. بعد ذلك، عليك تفعيل الفوترة في Cloud Console من أجل استخدام موارد/واجهات برمجة تطبيقات Cloud. لن تكلفك تجربة هذا الدرس التطبيقي حول الترميز الكثير من المال، إن لم تكلفك شيئًا على الإطلاق. لإيقاف الموارد كي لا يتم تحصيل رسوم منك بعد هذا الدرس التطبيقي حول الترميز، اتّبِع أي تعليمات "تنظيف" واردة في نهاية الدرس. يمكن لمستخدمي Google Cloud الجدد الاستفادة من برنامج الفترة التجريبية المجانية بقيمة 300 دولار أمريكي.

بدء Cloud Shell

على الرغم من إمكانية تشغيل Google Cloud عن بُعد من الكمبيوتر المحمول، ستستخدم في هذا الدرس العملي Google Cloud Shell، وهي بيئة سطر أوامر تعمل في السحابة الإلكترونية.

من وحدة تحكّم Google Cloud Platform، انقر على رمز Cloud Shell في شريط الأدوات العلوي الأيسر:

bce75f34b2c53987.png

لن يستغرق توفير البيئة والاتصال بها سوى بضع لحظات. عند الانتهاء، من المفترض أن يظهر لك ما يلي:

f6ef2b5f13479f3a.png

يتم تحميل هذه الآلة الافتراضية مزوّدة بكل أدوات التطوير التي ستحتاج إليها. توفّر هذه الخدمة دليلًا منزليًا ثابتًا بسعة 5 غيغابايت، وتعمل على Google Cloud، ما يؤدي إلى تحسين أداء الشبكة والمصادقة بشكل كبير. يمكن إكمال جميع المهام في هذا التمرين المعملي باستخدام متصفّح فقط.

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

ستحتاج إلى Cloud Scheduler لتشغيل خدمة Cloud Run على فترات منتظمة. تأكَّد من تفعيلها:

gcloud services enable cloudscheduler.googleapis.com

من المفترض أن تظهر لك العملية التي تمّت بنجاح:

Operation "operations/acf.5c5ef4f6-f734-455d-b2f0-ee70b5a17322" finished successfully.

4. استنساخ الرمز

استنسِخ الرمز البرمجي، إذا لم يسبق لك ذلك في تجربة الترميز السابقة:

git clone https://github.com/GoogleCloudPlatform/serverless-photosharing-workshop

يمكنك بعد ذلك الانتقال إلى الدليل الذي يحتوي على الخدمة:

cd serverless-photosharing-workshop/services/collage/nodejs

سيكون لديك تنسيق الملف التالي للخدمة:

services
 |
 ├── collage
      |
      ├── nodejs
           |
           ├── Dockerfile
           ├── index.js
           ├── package.json

داخل المجلد، لديك 3 ملفات:

  • يحتوي index.js على رمز Node.js
  • تحدّد السمة package.json العناصر التابعة للمكتبة
  • Dockerfile تحدّد صورة الحاوية

5- استكشاف الرمز

الاعتمادية

يحدّد ملف package.json الاعتمادات المطلوبة للمكتبة:

{
  "name": "collage_service",
  "version": "0.0.1",
  "main": "index.js",
  "scripts": {
    "start": "node index.js"
  },
  "dependencies": {
    "bluebird": "^3.7.2",
    "express": "^4.17.1",
    "imagemagick": "^0.1.3",
    "@google-cloud/firestore": "^4.9.9",
    "@google-cloud/storage": "^5.8.3"
  }
}

نعتمد على مكتبة Cloud Storage لقراءة ملفات الصور وحفظها في Cloud Storage. نحدّد تبعية على Cloud Firestore لجلب بيانات تعريف الصور التي خزّناها سابقًا. ‫Express هو إطار عمل JavaScript / Node للويب. يتم استخدام Bluebird للتعامل مع الوعود، وimagemagick هي مكتبة لمعالجة الصور.

ملف شامل

تحدّد Dockerfile صورة الحاوية للتطبيق:

FROM node:14-slim

# installing Imagemagick
RUN set -ex; \
  apt-get -y update; \
  apt-get -y install imagemagick; \
  rm -rf /var/lib/apt/lists/*

WORKDIR /picadaily/services/collage
COPY package*.json ./
RUN npm install --production
COPY . .
CMD [ "npm", "start" ]

نستخدم صورة أساسية خفيفة الوزن من Node 14. نحن بصدد تثبيت مكتبة imagemagick. بعد ذلك، نثبّت وحدات NPM التي تحتاج إليها التعليمات البرمجية، ونشغّل تعليمات node البرمجية باستخدام npm start.

index.js

لنلقِ نظرة عن كثب على رمز index.js:

const express = require('express');
const imageMagick = require('imagemagick');
const Promise = require("bluebird");
const path = require('path');
const {Storage} = require('@google-cloud/storage');
const Firestore = require('@google-cloud/firestore');

نحتاج إلى التبعيات المختلفة اللازمة لتشغيل برنامجنا: Express هو إطار عمل الويب Node الذي سنستخدمه، وImageMagick هي المكتبة اللازمة لمعالجة الصور، وBluebird هي مكتبة للتعامل مع الوعود في JavaScript، ويتم استخدام Path للتعامل مع مسارات الملفات والأدلة، ثم يتم استخدام Storage وFirestore للعمل على التوالي مع Google Cloud Storage (حِزم الصور) ومخزن بيانات Cloud Firestore.

const app = express();

app.get('/', async (req, res) => {
    try {
        console.log('Collage request');

        /* ... */

    } catch (err) {
        console.log(`Error: creating the collage: ${err}`);
        console.error(err);
        res.status(500).send(err);
    }
});

في ما يلي بنية معالج Node: يستجيب تطبيقنا لطلبات HTTP GET. ونحن نُجري بعض عمليات معالجة الأخطاء في حال حدوث خطأ ما. لنلقِ نظرة الآن على ما يتضمّنه هذا الهيكل.

const thumbnailFiles = [];
const pictureStore = new Firestore().collection('pictures');
const snapshot = await pictureStore
    .where('thumbnail', '==', true)
    .orderBy('created', 'desc')
    .limit(4).get();

if (snapshot.empty) {
    console.log('Empty collection, no collage to make');
    res.status(204).send("No collage created.");
} else {

    /* ... */

}

ستحتاج خدمة الصور المجمّعة إلى أربع صور على الأقل (تم إنشاء صورها المصغّرة)، لذا احرص على تحميل 4 صور أولاً.

نسترجع أحدث 4 صور حمّلها المستخدمون من البيانات الوصفية المخزّنة في Cloud Firestore. نتحقّق مما إذا كانت المجموعة الناتجة فارغة أم لا، ثم نتابع في فرع else من الرمز البرمجي.

لنجمع قائمة بأسماء الملفات:

snapshot.forEach(doc => {
    thumbnailFiles.push(doc.id);
});
console.log(`Picture file names: ${JSON.stringify(thumbnailFiles)}`);

سننزّل كل ملف من هذه الملفات من حزمة الصور المصغّرة، والتي يأتي اسمها من متغيّر بيئة نحدّده في وقت النشر:

const thumbBucket = storage.bucket(process.env.BUCKET_THUMBNAILS);

await Promise.all(thumbnailFiles.map(async fileName => {
    const filePath = path.resolve('/tmp', fileName);
    await thumbBucket.file(fileName).download({
        destination: filePath
    });
}));
console.log('Downloaded all thumbnails');

بعد تحميل أحدث الصور المصغّرة، سنستخدم مكتبة ImageMagick لإنشاء شبكة 4x4 من صور تلك الصور المصغّرة. نستخدم مكتبة Bluebird وتنفيذ Promise الخاص بها لتحويل الرمز البرمجي المستند إلى معاودة الاتصال إلى رمز برمجي متوافق مع async / await، ثم ننتظر الوعد الذي ينشئ تجميع الصور:

const collagePath = path.resolve('/tmp', 'collage.png');

const thumbnailPaths = thumbnailFiles.map(f => path.resolve('/tmp', f));
const convert = Promise.promisify(im.convert);
await convert([
    '(', ...thumbnailPaths.slice(0, 2), '+append', ')',
    '(', ...thumbnailPaths.slice(2), '+append', ')',
    '-size', '400x400', 'xc:none', '-background', 'none',  '-append',
    collagePath]);
console.log("Created local collage picture");

بما أنّ صورة الملصقة تم حفظها على القرص محليًا في المجلد المؤقت، علينا الآن تحميلها إلى Cloud Storage، ثم عرض ردّ ناجح (رمز الحالة 2xx):

await thumbBucket.upload(collagePath);
console.log("Uploaded collage to Cloud Storage bucket ${process.env.BUCKET_THUMBNAILS}");

res.status(204).send("Collage created.");

حان الوقت الآن لجعل نص Node البرمجي يستمع إلى الطلبات الواردة:

const PORT = process.env.PORT || 8080;

app.listen(PORT, () => {
    console.log(`Started collage service on port ${PORT}`);
});

في نهاية ملف المصدر، لدينا التعليمات التي تجعل Express يبدأ تطبيق الويب على المنفذ التلقائي 8080.

6. الاختبار محليًا

اختبِر الرمز برمجيًا على جهازك للتأكّد من أنّه يعمل قبل نشره على السحابة الإلكترونية.

داخل المجلد collage/nodejs، ثبِّت التبعيات npm وابدأ الخادم:

npm install; npm start

إذا سارت الأمور على ما يرام، من المفترض أن يبدأ الخادم على المنفذ 8080:

Started collage service on port 8080

استخدِم CTRL-C للخروج.

7. إنشاء تطبيق ونشره على Cloud Run

قبل النشر على Cloud Run، اضبط منطقة Cloud Run على إحدى المناطق المتوافقة والمنصة على managed:

gcloud config set run/region europe-west1
gcloud config set run/platform managed

يمكنك التأكّد من ضبط الإعدادات باتّباع الخطوات التالية:

gcloud config list

...
[run]
platform = managed
region = europe-west1

بدلاً من إنشاء صورة الحاوية ونشرها يدويًا باستخدام Cloud Build، يمكنك أيضًا الاعتماد على Cloud Run لإنشاء صورة الحاوية نيابةً عنك باستخدام Google Cloud Buildpacks.

نفِّذ الأمر التالي لإنشاء صورة الحاوية:

BUCKET_THUMBNAILS=thumbnails-$GOOGLE_CLOUD_PROJECT
SERVICE_NAME=collage-service
gcloud run deploy $SERVICE_NAME \
    --source . \
    --no-allow-unauthenticated \
    --update-env-vars BUCKET_THUMBNAILS=$BUCKET_THUMBNAILS

لاحظوا العلم –-source. هذا هو النشر المستند إلى المصدر في Cloud Run. إذا كان الملف Dockerfile متوفّرًا في دليل رمز المصدر، سيتم إنشاء رمز المصدر الذي تم تحميله باستخدام هذا الملف.Dockerfile في حال عدم توفّر Dockerfile في دليل رمز المصدر، تتعرّف حِزم الإنشاء في Google Cloud تلقائيًا على اللغة التي تستخدمها وتجلب تبعيات الرمز لإنشاء صورة حاوية جاهزة للإنتاج، وذلك باستخدام صورة أساسية آمنة تديرها Google. يُعلم هذا الخيار Cloud Run باستخدام حِزم Google Cloud Buildpacks لإنشاء صورة الحاوية المحدّدة في Dockerfile.

يُرجى العِلم أيضًا أنّ عملية النشر المستندة إلى المصدر تستخدم Artifact Registry لتخزين الحاويات التي تم إنشاؤها. ‫Artifact Registry هو إصدار حديث من Google Container Registry. سيطلب منك واجهة سطر الأوامر تفعيل واجهة برمجة التطبيقات إذا لم تكن مفعّلة في المشروع، كما ستنشئ مستودعًا بالاسم cloud-run-source-deploy في المنطقة التي تنشر فيها.

تجعل العلامة --no-allow-unauthenticated خدمة Cloud Run خدمة داخلية لن يتم تشغيلها إلا من خلال حسابات خدمة محدّدة.

8. إعداد Cloud Scheduler

بعد أن أصبحت خدمة Cloud Run جاهزة وتم نشرها، حان الوقت لإنشاء الجدول الزمني العادي، وذلك لاستدعاء الخدمة كل دقيقة.

أنشئ حساب خدمة:

SERVICE_ACCOUNT=collage-scheduler-sa
gcloud iam service-accounts create $SERVICE_ACCOUNT \
   --display-name "Collage Scheduler Service Account"

امنح حساب الخدمة الإذن باستدعاء خدمة Cloud Run:

gcloud run services add-iam-policy-binding $SERVICE_NAME \
   --member=serviceAccount:$SERVICE_ACCOUNT@$GOOGLE_CLOUD_PROJECT.iam.gserviceaccount.com \
   --role=roles/run.invoker

أنشئ مهمة Cloud Scheduler لتنفيذها كل دقيقة:

SERVICE_URL=$(gcloud run services describe $SERVICE_NAME --format 'value(status.url)')
gcloud scheduler jobs create http $SERVICE_NAME-job --schedule "* * * * *" \
   --http-method=GET \
   --location=europe-west1 \
   --uri=$SERVICE_URL \
   --oidc-service-account-email=$SERVICE_ACCOUNT@$GOOGLE_CLOUD_PROJECT.iam.gserviceaccount.com \
   --oidc-token-audience=$SERVICE_URL

يمكنك الانتقال إلى قسم Cloud Scheduler في Cloud Console للتأكّد من إعداده وتوجيهه إلى عنوان URL الخاص بخدمة Cloud Run:

35119e28c1da53f3.png

9- اختبار الخدمة

لاختبار ما إذا كان الإعداد يعمل، ابحث في الحزمة thumbnails عن صورة المجمّعة (المسمّاة collage.png). يمكنك أيضًا الاطّلاع على سجلّات الخدمة:

93922335a384be2e.png

10. التنظيف (اختياري)

إذا كنت لا تنوي مواصلة استخدام المختبرات الأخرى في السلسلة، يمكنك تنظيف الموارد لتوفير التكاليف ولتكون مواطنًا جيدًا للخدمات السحابية بشكل عام. يمكنك تنظيف الموارد بشكل فردي على النحو التالي.

احذف الخدمة باتّباع الخطوات التالية:

gcloud run services delete $SERVICE_NAME -q

احذف مهمة Cloud Scheduler:

gcloud scheduler jobs delete $SERVICE_NAME-job -q

بدلاً من ذلك، يمكنك حذف المشروع بأكمله باتّباع الخطوات التالية:

gcloud projects delete $GOOGLE_CLOUD_PROJECT

11. تهانينا!

تهانينا! لقد أنشأت خدمة مجدولة: بفضل Cloud Scheduler الذي يرسل رسالة كل دقيقة إلى موضوع Pub/Sub، يتم استدعاء خدمة تجميع الصور في Cloud Run ويمكنها إضافة الصور معًا لإنشاء الصورة الناتجة.

المواضيع التي تناولناها

  • Cloud Run
  • Cloud Scheduler
  • Cloud Storage
  • Cloud Firestore

الخطوات التالية