Pic-à-quoi : Atelier 3 : Créer un montage des photos les plus récentes

1. Présentation

Dans cet atelier de programmation, vous allez créer un service Cloud Run, collage service, qui sera déclenché par Cloud Scheduler à intervalles réguliers. Le service récupère les dernières photos importées et crée un montage à partir de celles-ci : il recherche la liste des photos récentes dans Cloud Firestore, puis télécharge les fichiers image réels à partir de Cloud Storage.

df20f5d0402b54b4.png

Points abordés

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

2. Préparation

Configuration de l'environnement au rythme de chacun

  1. Connectez-vous à la console Google Cloud, puis créez un projet ou réutilisez un projet existant. (Si vous ne possédez pas encore de compte Gmail ou Google Workspace, vous devez en créer un.)

96a9c957bc475304.png

b9a10ebdf5b5a448.png

a1e3c01a38fa61c2.png

  • Le nom du projet est le nom à afficher pour les participants au projet. Il s'agit d'une chaîne de caractères qui n'est pas utilisée par les API Google, et que vous pouvez modifier à tout moment.
  • L'ID du projet doit être unique sur l'ensemble des projets Google Cloud et doit être immuable (vous ne pouvez pas le modifier une fois que vous l'avez défini). Cloud Console génère automatiquement une chaîne unique dont la composition importe peu, en général. Dans la plupart des ateliers de programmation, vous devrez référencer l'ID du projet (généralement identifié comme PROJECT_ID), donc s'il ne vous convient pas, générez-en un autre au hasard ou définissez le vôtre, puis vérifiez s'il est disponible. Il est ensuite "gelé" une fois le projet créé.
  • La troisième valeur est le numéro de projet, utilisé par certaines API. Pour en savoir plus sur ces trois valeurs, consultez la documentation.
  1. Vous devez ensuite activer la facturation dans Cloud Console afin d'utiliser les ressources/API Cloud. L'exécution de cet atelier de programmation est très peu coûteuse, voire sans frais. Pour arrêter les ressources afin d'éviter qu'elles ne vous soient facturées après ce tutoriel, suivez les instructions de nettoyage indiquées à la fin de l'atelier. Les nouveaux utilisateurs de Google Cloud peuvent participer au programme d'essai sans frais pour bénéficier d'un crédit de 300$.

Démarrer Cloud Shell

Bien que Google Cloud puisse être utilisé à distance depuis votre ordinateur portable, nous allons nous servir de Google Cloud Shell pour cet atelier de programmation, un environnement de ligne de commande exécuté dans le cloud.

Depuis la console GCP, cliquez sur l'icône Cloud Shell de la barre d'outils située dans l'angle supérieur droit :

bce75f34b2c53987.png

Le provisionnement et la connexion à l'environnement prennent quelques instants seulement. Une fois l'opération terminée, le résultat devrait ressembler à ceci :

f6ef2b5f13479f3a.png

Cette machine virtuelle contient tous les outils de développement nécessaires. Elle comprend un répertoire d'accueil persistant de 5 Go et s'exécute sur Google Cloud, ce qui améliore nettement les performances du réseau et l'authentification. Vous pouvez réaliser toutes les activités de cet atelier dans un simple navigateur.

3. Activer les API

Vous aurez besoin d'un Cloud Scheduler pour déclencher le service Cloud Run à intervalles réguliers. Assurez-vous qu'il est activé :

gcloud services enable cloudscheduler.googleapis.com

L'opération devrait se terminer correctement :

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

4. Cloner le code

Clonez le code, si vous ne l'avez pas déjà fait dans l'atelier de programmation précédent :

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

Vous pouvez ensuite accéder au répertoire contenant le service :

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

Votre service utilisera la structure de fichiers suivante :

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

Le dossier contient trois fichiers :

  • index.js contient le code Node.js.
  • package.json définit les dépendances de la bibliothèque.
  • Dockerfile définit l'image du conteneur.

5. Explorer le code

Dépendances

Le fichier package.json définit les dépendances de bibliothèque nécessaires :

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

Nous dépendons de la bibliothèque Cloud Storage pour lire et enregistrer les fichiers image dans Cloud Storage. Nous déclarons une dépendance sur Cloud Firestore pour récupérer les métadonnées des photos que nous avons stockées précédemment. Express est un framework Web JavaScript / Node. Bluebird est utilisé pour gérer les promesses, et imagemagick est une bibliothèque permettant de manipuler des images.

Dockerfile

Dockerfile définit l'image de conteneur pour l'application :

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

Nous utilisons une image de base Node 14 légère. Nous installons la bibliothèque imagemagick. Nous installons ensuite les modules NPM nécessaires à notre code et exécutons notre code Node avec npm start.

index.js

Examinons de plus près notre code 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');

Nous avons besoin des différentes dépendances nécessaires à l'exécution de notre programme : Express est le framework Web Node que nous allons utiliser, ImageMagick est la bibliothèque de manipulation d'images, Bluebird est une bibliothèque de gestion des promesses JavaScript, Path est utilisé pour gérer les chemins d'accès aux fichiers et aux répertoires, et Storage et Firestore servent respectivement à travailler avec Google Cloud Storage (nos buckets d'images) et le data store 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);
    }
});

Ci-dessus, nous avons la structure de notre gestionnaire Node : notre application répond aux requêtes HTTP GET. Nous gérons également les erreurs au cas où quelque chose se passe mal. Examinons maintenant le contenu de cette structure.

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 {

    /* ... */

}

Notre service de montage a besoin d'au moins quatre images (dont les vignettes ont été générées). Veillez donc à en importer quatre au préalable.

Nous récupérons les quatre dernières photos importées par nos utilisateurs à partir des métadonnées stockées dans Cloud Firestore. Nous vérifions si la collection résultante est vide ou non, puis nous poursuivons dans la branche "else" de notre code.

Collectons la liste des noms de fichiers :

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

Nous allons télécharger chacun de ces fichiers à partir du bucket de miniatures, dont le nom provient d'une variable d'environnement que nous avons définie au moment du déploiement :

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

Une fois les dernières miniatures importées, nous allons utiliser la bibliothèque ImageMagick pour créer une grille 4x4 de ces miniatures. Nous utilisons la bibliothèque Bluebird et son implémentation Promise pour transformer le code basé sur les rappels en code compatible avec async / await, puis nous attendons la promesse qui crée le collage d'images :

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

Comme l'image du collage a été enregistrée sur le disque local dans le dossier temporaire, nous devons maintenant l'importer dans Cloud Storage, puis renvoyer une réponse positive (code d'état 2xx) :

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

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

Il est maintenant temps de faire en sorte que notre script Node écoute les requêtes entrantes :

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

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

À la fin de notre fichier source, nous avons les instructions permettant à Express de démarrer notre application Web sur le port 8080 par défaut.

6. Tester en local

Testez le code en local pour vous assurer qu'il fonctionne avant de le déployer dans le cloud.

Dans le dossier collage/nodejs, installez les dépendances npm et démarrez le serveur :

npm install; npm start

Si tout s'est bien passé, le serveur devrait démarrer sur le port 8080 :

Started collage service on port 8080

Utilisez CTRL-C pour quitter.

7. Créer et déployer sur Cloud Run

Avant de déployer sur Cloud Run, définissez la région Cloud Run sur l'une des régions compatibles et la plate-forme sur managed :

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

Vous pouvez vérifier que la configuration est définie :

gcloud config list

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

Au lieu de créer et de publier manuellement l'image de conteneur à l'aide de Cloud Build, vous pouvez également vous appuyer sur Cloud Run pour créer l'image de conteneur à l'aide des buildpacks Google Cloud.

Exécutez la commande suivante pour créer l'image de conteneur :

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

Notez l'indicateur –-source. Il s'agit du déploiement basé sur la source dans Cloud Run. Si un Dockerfile est présent dans le répertoire du code source, le code source importé est créé à l'aide de ce Dockerfile. Si aucun Dockerfile n'est présent dans le répertoire du code source, les buildpacks Google Cloud détectent automatiquement le langage que vous utilisez et extraient les dépendances du code pour créer une image de conteneur prête pour la production, à l'aide d'une image de base sécurisée et gérée par Google. Cela indique à Cloud Run d'utiliser les buildpacks Google Cloud pour créer l'image de conteneur définie dans Dockerfile.

Notez également que le déploiement basé sur la source utilise Artifact Registry pour stocker les conteneurs créés. Artifact Registry est une version moderne de Google Container Registry. L'interface CLI vous invite à activer l'API si elle ne l'est pas déjà dans le projet, et crée un dépôt nommé cloud-run-source-deploy dans la région où vous effectuez le déploiement.

L'indicateur --no-allow-unauthenticated fait du service Cloud Run un service interne qui ne sera déclenché que par des comptes de service spécifiques.

8. Configurer Cloud Scheduler

Maintenant que le service Cloud Run est prêt et déployé, il est temps de créer la planification régulière pour appeler le service toutes les minutes.

Créez un compte de service :

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

Autorisez le compte de service à appeler le service 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

Créez une tâche Cloud Scheduler à exécuter toutes les minutes :

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

Vous pouvez accéder à la section Cloud Scheduler de la console Cloud pour vérifier qu'il est configuré et qu'il pointe vers l'URL du service Cloud Run :

35119e28c1da53f3.png

9. Tester le service

Pour vérifier que la configuration fonctionne, recherchez l'image du collage (appelée collage.png) dans le bucket thumbnails. Vous pouvez également consulter les journaux du service :

93922335a384be2e.png

10. Nettoyer (facultatif)

Si vous n'avez pas l'intention de continuer à suivre les autres ateliers de cette série, vous pouvez nettoyer les ressources pour limiter vos dépenses et utiliser le cloud de manière raisonnée. Vous pouvez nettoyer les ressources individuellement comme suit.

Supprimez le service :

gcloud run services delete $SERVICE_NAME -q

Supprimez le job Cloud Scheduler :

gcloud scheduler jobs delete $SERVICE_NAME-job -q

Vous pouvez également supprimer l'intégralité du projet :

gcloud projects delete $GOOGLE_CLOUD_PROJECT

11. Félicitations !

Félicitations ! Vous avez créé un service planifié : grâce à Cloud Scheduler, qui envoie un message toutes les minutes sur un sujet Pub/Sub, votre service de montage Cloud Run est appelé et peut ajouter des images pour créer l'image finale.

Points abordés

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

Étapes suivantes