תמונה יומית: שיעור 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.png

b9a10ebdf5b5a448.png

a1e3c01a38fa61c2.png

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

הפעלת Cloud Shell

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

ממסוף GCP, לוחצים על הסמל של 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. שכפול הקוד

משכפלים את הקוד, אם עדיין לא עשיתם זאת ב-Code Lab הקודם:

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. כחול-בירד משמש לטיפול בהבטחות, ו-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.

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 הצמתים: האפליקציה מגיבה לבקשות 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 התמונות האחרונות שהמשתמשים שלנו העלו, מהמטא-נתונים שמאוחסנים ב-Cloud Firerstore. אנחנו בודקים אם האוסף שנוצר ריק או לא, ואז ממשיכים בהסתעפות האחרת של הקוד.

בואו נאסוף את הרשימה של שמות הקבצים:

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 וביישום Promis שלה כדי להפוך את הקוד שמבוסס על קריאה חוזרת לקוד ידידותי ל-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.");

עכשיו הזמן לגרום לסקריפט הצומת להקשיב לבקשות נכנסות:

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

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

35119e28c1da53f3.png

9. בדיקת השירות

כדי לבדוק אם ההגדרה עובדת, בודקים בקטגוריה thumbnails את תמונת הקולאז' (שנקראת collage.png). אפשר גם לבדוק את יומני השירות:

93922335a384be2e.png

10. הסרת המשאבים (אופציונלי)

אם אתם לא מתכוונים להמשיך עם שיעורי ה-Lab האחרים בסדרה, תוכלו לפנות משאבים כדי לחסוך בעלויות ולהיות אזרחי הענן באופן כללי. אפשר למחוק משאבים בנפרד באופן הבא.

מוחקים את השירות:

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

השלבים הבאים