Pic-a-daily: Lab 3 - Crea un collage delle immagini più recenti

1. Panoramica

In questo lab di codice creerai un nuovo servizio Cloud Run, il servizio di collage, che verrà attivato da Cloud Scheduler a intervalli regolari. Il servizio recupera le immagini più recenti caricate e crea un collage di queste immagini: trova l'elenco delle immagini recenti in Cloud Firestore e scarica i file delle immagini effettive da Cloud Storage.

df20f5d0402b54b4.png

Cosa imparerai a fare

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

2. Configurazione e requisiti

Configurazione dell'ambiente da seguire in modo autonomo

  1. Accedi alla console Google Cloud e crea un nuovo progetto o riutilizzane uno esistente. Se non hai ancora un account Gmail o Google Workspace, devi crearne uno.

96a9c957bc475304.png

b9a10ebdf5b5a448.png

a1e3c01a38fa61c2.png

  • Il Nome progetto è il nome visualizzato dei partecipanti del progetto. Si tratta di una stringa di caratteri non utilizzata dalle API di Google e può essere aggiornata in qualsiasi momento.
  • L'ID progetto deve essere univoco in tutti i progetti Google Cloud ed è immutabile (non può essere modificato dopo essere stato impostato). La console Cloud genera automaticamente una stringa univoca. di solito non ti importa cosa sia. Nella maggior parte dei codelab, devi fare riferimento all'ID progetto (che solitamente è identificato come PROJECT_ID), quindi, se non ti piace, generane un altro a caso oppure puoi fare un tentativo personalizzato e controllare se è disponibile. Poi c'è "congelato" dopo la creazione del progetto.
  • C'è un terzo valore, il numero di progetto, utilizzato da alcune API. Scopri di più su tutti e tre questi valori nella documentazione.
  1. Successivamente, dovrai abilitare la fatturazione nella console Cloud per utilizzare le risorse/le API Cloud. Eseguire questo codelab non dovrebbe costare molto. Per arrestare le risorse in modo da non incorrere in fatturazione oltre questo tutorial, segui eventuali "pulizie" istruzioni riportate alla fine del codelab. I nuovi utenti di Google Cloud sono idonei al programma prova senza costi di 300$.

Avvia Cloud Shell

Anche se Google Cloud può essere utilizzato da remoto dal tuo laptop, in questo codelab utilizzerai Google Cloud Shell, un ambiente a riga di comando in esecuzione nel cloud.

Dalla console di Google Cloud, fai clic sull'icona di Cloud Shell nella barra degli strumenti in alto a destra:

bce75f34b2c53987.png

Dovrebbe richiedere solo qualche istante per eseguire il provisioning e connettersi all'ambiente. Al termine, dovresti vedere una schermata simile al seguente:

f6ef2b5f13479f3a.png

Questa macchina virtuale viene caricata con tutti gli strumenti di sviluppo necessari. Offre una home directory permanente da 5 GB e viene eseguita su Google Cloud, migliorando notevolmente le prestazioni di rete e l'autenticazione. Tutto il lavoro in questo lab può essere svolto semplicemente con un browser.

3. Abilita API

Avrai bisogno di Cloud Scheduler per attivare il servizio Cloud Run a intervalli regolari. Assicurati che sia abilitata:

gcloud services enable cloudscheduler.googleapis.com

L'operazione dovrebbe essere completata correttamente:

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

4. Clona il codice

Clona il codice, se non l'hai già fatto nel codelab precedente:

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

Quindi puoi andare alla directory contenente il servizio:

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

Per il servizio avrai il seguente layout di file:

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

La cartella contiene tre file:

  • index.js contiene il codice Node.js
  • package.json definisce le dipendenze della libreria
  • Dockerfile definisce l'immagine container

5. Esplora il codice

Dipendenze

Il file package.json definisce le dipendenze di libreria necessarie:

{
  "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"
  }
}

Dipendiamo dalla libreria di Cloud Storage per leggere e salvare i file immagine all'interno di Cloud Storage. Dichiariamo una dipendenza su Cloud Firestore per recuperare i metadati delle immagini archiviati in precedenza. Express è un framework web JavaScript / Nodo. Bluebird viene utilizzato per gestire le promesse, mentre imagemagick è una libreria per la manipolazione di immagini.

Dockerfile

Dockerfile definisce l'immagine container per l'applicazione:

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" ]

Stiamo usando un'immagine base Node 14 chiara. Stiamo installando la libreria Imagemagick. Quindi installiamo i moduli di Gestione dei partner di rete necessari per il nostro codice ed eseguiamo il codice nodo con npm start.

index.js

Diamo un'occhiata più da vicino al nostro codice 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');

Abbiamo bisogno delle varie dipendenze necessarie per l'esecuzione del nostro programma: Express è il framework web Node che utilizzeremo, ImageMagick la libreria per la manipolazione delle immagini, Bluebird è una libreria per la gestione delle promesse di JavaScript, Path viene utilizzato per gestire i percorsi di file e directory e quindi Storage e Firestore sono utilizzati per lavorare rispettivamente con Google Cloud Storage (i nostri bucket di immagini) e con il datastore 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);
    }
});

Qui sopra abbiamo la struttura del gestore dei nodi: la nostra app risponde alle richieste HTTP GET. Stiamo elaborando anche un po' di gestione degli errori nel caso in cui qualcosa vada storto. Diamo ora un'occhiata a cosa c'è all'interno di questa struttura.

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 {

    /* ... */

}

Il nostro servizio di collage necessita di almeno quattro immagini (le cui miniature sono state generate), quindi assicurati di caricare prima quattro immagini.

Recuperiamo le 4 immagini più recenti caricate dai nostri utenti dai metadati archiviati in Cloud Firerstore. Verifichiamo se la raccolta risultante è vuota o meno, quindi procediamo nel ramo altro del nostro codice.

Raccogliamo l'elenco dei nomi di file:

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

Scarichiamo ognuno di questi file dal bucket delle miniature, il cui nome deriva da una variabile di ambiente che abbiamo impostato al momento del deployment:

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');

Una volta caricate le miniature più recenti, utilizzeremo la libreria ImageMagick per creare una griglia 4x4 con le miniature. Utilizziamo la libreria Bluebird e la sua implementazione Promise per trasformare il codice basato su callback in un codice semplice da async / await, quindi aspettiamo la promessa che sta creando il collage di immagini:

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");

Poiché l'immagine del collage è stata salvata sul disco localmente nella cartella temporanea, dobbiamo caricarla in Cloud Storage e quindi restituire una risposta positiva (codice di stato 2xx):

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

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

Ora è il momento di fare in modo che il nostro script del nodo ascolti le richieste in arrivo:

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

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

Alla fine del nostro file sorgente, abbiamo le istruzioni per fare in modo che Express avvii la nostra applicazione web sulla porta predefinita 8080.

6. Esegui test in locale

Testa il codice in locale per assicurarti che funzioni prima di eseguirne il deployment nel cloud.

All'interno della cartella collage/nodejs, installa le dipendenze npm e avvia il server:

npm install; npm start

Se tutto è andato a buon fine, il server dovrebbe essere avviato sulla porta 8080:

Started collage service on port 8080

Usa CTRL-C per uscire.

7. Crea ed esegui il deployment su Cloud Run

Prima di eseguire il deployment in Cloud Run, imposta la regione di Cloud Run su una delle regioni e della piattaforma supportate su managed:

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

Puoi verificare che la configurazione sia impostata:

gcloud config list

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

Anziché creare e pubblicare manualmente l'immagine container con Cloud Build, puoi fare affidamento su Cloud Run per creare l'immagine container al posto tuo utilizzando Google Cloud Buildpacks.

Esegui questo comando per creare l'immagine container:

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

Nota il flag –-source. Questo è il deployment basato sull'origine in Cloud Run. Se nella directory del codice sorgente è presente un Dockerfile, il codice sorgente caricato viene creato utilizzando questo Dockerfile. Se nella directory del codice sorgente non è presente alcun Dockerfile, i buildpacks di Google Cloud rilevano automaticamente il linguaggio in uso e recupera le dipendenze del codice per creare un'immagine container pronta per la produzione, utilizzando un'immagine base sicura gestita da Google. Questo flag segnala Cloud Run per utilizzare Google Cloud Buildpacks per creare l'immagine container definita in Dockerfile.

Tieni inoltre presente che il deployment basato sull'origine utilizza Artifact Registry per archiviare i container creati. Artifact Registry è una versione moderna di Google Container Registry. L'interfaccia a riga di comando ti chiederà di abilitare l'API, se non è già abilitata nel progetto, e creerà un repository con il nome cloud-run-source-deploy nella regione in cui stai eseguendo il deployment.

Il flag --no-allow-unauthenticated rende il servizio Cloud Run un servizio interno che viene attivato solo da account di servizio specifici.

8. Configura Cloud Scheduler

Ora che il servizio Cloud Run è pronto ed è stato eseguito il deployment, è il momento di creare la pianificazione regolare, per richiamare il servizio ogni minuto.

Crea un account di servizio:

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

Concedi all'account di servizio l'autorizzazione per richiamare il servizio 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

Crea un job Cloud Scheduler da eseguire ogni minuto:

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

Puoi andare alla sezione Cloud Scheduler in Cloud Console per verificare che sia configurato e che punti all'URL del servizio Cloud Run:

35119e28c1da53f3.png

9. Prova il servizio

Per verificare se la configurazione funziona, cerca l'immagine del collage nel bucket thumbnails (denominata collage.png). Puoi anche controllare i log del servizio:

93922335a384be2e.png

10. Libera spazio (facoltativo)

Se non intendi continuare con gli altri lab della serie, puoi eseguire la pulizia delle risorse per risparmiare sui costi ed essere nel complesso un buon cittadino del cloud. Puoi eseguire la pulizia delle risorse singolarmente come descritto di seguito.

Elimina il servizio:

gcloud run services delete $SERVICE_NAME -q

Elimina il job Cloud Scheduler:

gcloud scheduler jobs delete $SERVICE_NAME-job -q

In alternativa, puoi eliminare l'intero progetto:

gcloud projects delete $GOOGLE_CLOUD_PROJECT

11. Complimenti!

Complimenti! Hai creato un servizio pianificato: grazie a Cloud Scheduler, che esegue il push di un messaggio ogni minuto su un argomento Pub/Sub, il servizio di collage Cloud Run viene richiamato ed è in grado di aggiungere immagini per creare l'immagine risultante.

Argomenti trattati

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

Passaggi successivi