תמונה יומית: שיעור Lab 3 – יצירת קולאז' של התמונות האחרונות

1. סקירה כללית

בשיעור ה-Lab הזה תיצרו שירות חדש של Cloud Run, שירות קולאז', שיופעל על ידי Cloud Scheduler במרווחי זמן קבועים. השירות מאחזר את התמונות האחרונות שהועלו ויוצר מהן קולאז': הוא מוצא את רשימת התמונות האחרונות ב-Cloud Firestore, ואז מוריד את קובצי התמונות בפועל מ-Cloud Storage.

df20f5d0402b54b4.png

מה תלמדו

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

2. הגדרה ודרישות

הגדרת סביבה בקצב אישי

  1. נכנסים ל-מסוף Google Cloud ויוצרים פרויקט חדש או משתמשים בפרויקט קיים. אם עדיין אין לכם חשבון Gmail או Google Workspace, אתם צריכים ליצור חשבון.

96a9c957bc475304.pngb9a10ebdf5b5a448.pnga1e3c01a38fa61c2.png

  • שם הפרויקט הוא השם המוצג למשתתפים בפרויקט. זו מחרוזת של תווים שלא נמצאת בשימוש ב-Google APIs, ואפשר לעדכן אותה בכל שלב.
  • מזהה הפרויקט חייב להיות ייחודי לכל הפרויקטים ב-Google Cloud, והוא קבוע (אי אפשר לשנות אותו אחרי שהוא מוגדר). מסוף Cloud יוצר באופן אוטומטי מחרוזת ייחודית. בדרך כלל לא צריך לדעת מה היא. ברוב סדנאות ה-Codelab, תצטרכו להפנות למזהה הפרויקט (ובדרך כלל הוא מזוהה כ-PROJECT_ID), אז אם אתם לא אוהבים אותו, תוכלו ליצור מזהה אקראי אחר, או לנסות ליצור מזהה משלכם ולראות אם הוא זמין. אחרי שהפרויקט נוצר, הוא 'קפוא'.
  • יש ערך שלישי, מספר פרויקט, שמשמש חלק מממשקי ה-API. מידע נוסף על שלושת הערכים האלה מופיע במאמרי העזרה.
  1. בשלב הבא, תצטרכו להפעיל את החיוב במסוף Cloud כדי להשתמש במשאבי Cloud או בממשקי API. העלות של התרגול הזה לא אמורה להיות גבוהה, ואולי לא תהיה עלות בכלל. כדי לכבות את המשאבים ולא לחייב אתכם מעבר למה שמוסבר במדריך הזה, צריך לפעול לפי ההוראות לניקוי שמופיעות בסוף ה-Codelab. משתמשים חדשים ב-Google Cloud זכאים לתוכנית תקופת ניסיון בחינם בשווי 300$.

מפעילים את Cloud Shell

אפשר להפעיל את Google Cloud מרחוק מהמחשב הנייד, אבל ב-Codelab הזה נשתמש ב-Google Cloud Shell, סביבת שורת פקודה שפועלת בענן.

ב-GCP Console, לוחצים על סמל Cloud Shell בסרגל הכלים שבפינה הימנית העליונה:

bce75f34b2c53987.png

הקצאת המשאבים והחיבור לסביבה יימשכו רק כמה רגעים. בסיום התהליך, אמור להופיע משהו כזה:

f6ef2b5f13479f3a.png

המכונה הווירטואלית הזו כוללת את כל הכלים שדרושים למפתחים. יש בה ספריית בית בנפח מתמיד של 5GB והיא פועלת ב-Google Cloud, מה שמשפר מאוד את הביצועים והאימות ברשת. אפשר לבצע את כל העבודה ב-Lab הזה רק באמצעות דפדפן.

3. הפעלת ממשקי ה-API

תצטרכו 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 היא ספריה לשינוי תמונות.

קובץ Docker

‫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 שנדרשים לקוד שלנו, ומריצים את קוד הצומת באמצעות 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);
    }
});

למעלה אפשר לראות את המבנה של ה-handler של 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 בספריית קוד המקור, חבילות ה-Buildpack של Google Cloud מזהות באופן אוטומטי את השפה שבה אתם משתמשים ומאחזרות את התלויות של הקוד כדי ליצור קובץ אימג' של קונטיינר שמוכן לייצור, באמצעות קובץ אימג' בסיסי מאובטח שמנוהל על ידי Google. הפעולה הזו מסמנת ל-Cloud Run להשתמש ב-Google Cloud Buildpacks כדי ליצור את קובץ האימג' של הקונטיינר שמוגדר ב-Dockerfile.

חשוב גם לציין שפריסה מבוססת-מקור משתמשת ב-Artifact Registry כדי לאחסן קונטיינרים שנבנו. ‫Artifact Registry היא גרסה מודרנית של Google Container Registry. אם ה-API לא מופעל בפרויקט, ה-CLI יציג בקשה להפעיל אותו, וייצור מאגר בשם 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

השלבים הבאים