Tutoriel Utiliser Pub/Sub avec Cloud Run

Ce didacticiel explique comment écrire, déployer et appeler un service Cloud Run à partir d'un abonnement push Pub/Sub.

Vous pouvez utiliser ce tutoriel avec Cloud Run (entièrement géré) ou Cloud Run pour Anthos sur Google Cloud.

Objectifs

  • Écrire, créer et déployer un service sur Cloud Run ou Cloud Run pour Anthos sur Google Cloud
  • Appeler le service en publiant un message dans un sujet Pub/Sub.

Coûts

Ce tutoriel utilise des composants facturables de Cloud Platform, dont :

Obtenez une estimation des coûts en fonction de votre utilisation prévue à l'aide du simulateur de coût.

Les nouveaux utilisateurs de Cloud Platform peuvent bénéficier d'un essai gratuit.

Avant de commencer

  1. Connectez-vous à votre compte Google.

    Si vous n'en possédez pas déjà un, vous devez en créer un.

  2. Dans Cloud Console, sur la page de sélection du projet, sélectionnez ou créez un projet Cloud.

    Accéder à la page de sélection du projet

  3. Vérifiez que la facturation est activée pour votre projet Google Cloud. Découvrez comment vérifier que la facturation est activée pour votre projet.

  4. Activer l'API Cloud Run
  5. Installez et initialisez le SDK Cloud.
  6. Pour Cloud Run pour Anthos sur Google Cloud, installez le composant gcloud kubectl :
    gcloud components install kubectl
  7. Mettez à jour les composants :
    gcloud components update
  8. Si vous utilisez Cloud Run pour Anthos sur Google Cloud, créez un cluster en suivant les instructions de la section Configurer Cloud Run pour Anthos sur Google Cloud.

Configurer les paramètres par défaut de gcloud

Pour configurer gcloud avec les valeurs par défaut pour votre service Cloud Run, procédez comme suit :

  1. Définissez le projet par défaut :

        gcloud config set project PROJECT-ID

    Remplacez PROJECT-ID par le nom du projet que vous avez créé pour ce tutoriel.

  2. Si vous utilisez Cloud Run (entièrement géré), configurez gcloud pour la région choisie :

        gcloud config set run/region REGION

    Remplacez REGION par la région Cloud Run compatible de votre choix.

  3. Si vous utilisez Cloud Run pour Anthos sur Google Cloud, configurez gcloud pour votre cluster :

        gcloud config set run/cluster CLUSTER-NAME
        gcloud config set run/cluster_location REGION

    Remplacer

    • CLUSTER-NAME par le nom que vous avez utilisé pour votre cluster,
    • REGION par l'emplacement de cluster compatible de votre choix.

Emplacements Cloud Run

Cloud Run est régional, ce qui signifie que l'infrastructure qui exécute vos services Cloud Run est située dans une région spécifique, et gérée par Google pour être disponible de manière redondante dans toutes les zones de cette région.

Lors de la sélection de la région dans laquelle exécuter vos services Cloud Run, vous devez tout d'abord considérer vos exigences en termes de latence, de disponibilité et de durabilité. Vous pouvez généralement sélectionner la région la plus proche de vos utilisateurs, mais vous devez considérer l'emplacement des autres produits Google Cloud utilisés par votre service Cloud Run. L'utilisation conjointe de produits Google Cloud sur plusieurs sites peut avoir une incidence sur la latence et le coût de votre service.

Cloud Run est disponible dans les régions suivantes :

  • asia-east1 (Taïwan)
  • asia-northeast1 (Tokyo)
  • europe-north1 (Finlande)
  • europe-west1 (Belgique)
  • europe-west4 (Pays-Bas)
  • us-central1 (Iowa)
  • us-east1 (Caroline du Sud)
  • us-east4 (Virginie du Nord)
  • us-west1 (Oregon)

Si vous avez déjà créé un service Cloud Run, vous pouvez afficher la région dans le tableau de bord Cloud Run de Cloud Console.

Créer un sujet Pub/Sub

L'exemple de service est déclenché par les messages publiés sur un sujet Pub/Sub. Vous devez donc créer un sujet dans Pub/Sub.

Pour créer un sujet Pub/Sub, utilisez la commande suivante :

    gcloud pubsub topics create myRunTopic

Vous pouvez utiliser myRunTopic ou le remplacer par un nom de sujet unique dans votre projet Google Cloud.

Récupérer l'exemple de code

Pour récupérer l’exemple de code à utiliser, procédez comme suit :

  1. Clonez le dépôt de l'exemple d'application sur votre machine locale :

    Node.js

    git clone https://github.com/GoogleCloudPlatform/nodejs-docs-samples.git

    Vous pouvez également télécharger l'exemple en tant que fichier ZIP et l'extraire.

    Python

    git clone https://github.com/GoogleCloudPlatform/python-docs-samples.git

    Vous pouvez également télécharger l'exemple en tant que fichier ZIP et l'extraire.

    Go

    git clone https://github.com/GoogleCloudPlatform/golang-samples.git

    Vous pouvez également télécharger l'exemple en tant que fichier ZIP et l'extraire.

    Java

    git clone https://github.com/GoogleCloudPlatform/java-docs-samples.git

    Vous pouvez également télécharger l'exemple en tant que fichier ZIP et l'extraire.

  2. Accédez au répertoire contenant l'exemple de code de Cloud Run :

    Node.js

    cd nodejs-docs-samples/run/pubsub/

    Python

    cd python-docs-samples/run/pubsub/

    Go

    cd golang-samples/run/pubsub/

    Java

    cd java-docs-samples/run/pubsub/

Examiner le code

Le code de ce tutoriel comprend les éléments suivants :

  • Un serveur qui gère les requêtes entrantes.

    Node.js

    Pour que le service Node.js reste facile à tester, la configuration du serveur est distincte du démarrage du serveur.

    Le serveur Web Node.js est configuré dans app.js.

    const express = require('express');
        const bodyParser = require('body-parser');
        const app = express();
    
        app.use(bodyParser.json());
    Le serveur Web est démarré dans index.js :
    const app = require('./app.js');
        const PORT = process.env.PORT || 8080;
    
        app.listen(PORT, () =>
          console.log(`nodejs-pubsub-tutorial listening on port ${PORT}`)
        );

    Python

    import base64
        from flask import Flask, request
        import os
        import sys
    
        app = Flask(__name__)

    Go

    
        // Sample run-pubsub is a Cloud Run service which handles Pub/Sub messages.
        package main
    
        import (
        	"encoding/json"
        	"io/ioutil"
        	"log"
        	"net/http"
        	"os"
        )
    
        func main() {
        	http.HandleFunc("/", HelloPubSub)
        	// Determine port for HTTP service.
        	port := os.Getenv("PORT")
        	if port == "" {
        		port = "8080"
        		log.Printf("Defaulting to port %s", port)
        	}
        	// Start HTTP server.
        	log.Printf("Listening on port %s", port)
        	if err := http.ListenAndServe(":"+port, nil); err != nil {
        		log.Fatal(err)
        	}
        }
        

    Java

    import org.springframework.boot.SpringApplication;
        import org.springframework.boot.autoconfigure.SpringBootApplication;
    
        @SpringBootApplication
        public class PubSubApplication {
          public static void main(String[] args) {
            String port = System.getenv().getOrDefault("PORT", "8080");
            System.setProperty("server.port", port);
            SpringApplication.run(PubSubApplication.class, args);
          }
        }

  • Un gestionnaire qui traite le message Pub/Sub et enregistre un message d'accueil.

    Node.js

    app.post('/', (req, res) => {
          if (!req.body) {
            const msg = 'no Pub/Sub message received';
            console.error(`error: ${msg}`);
            res.status(400).send(`Bad Request: ${msg}`);
            return;
          }
          if (!req.body.message) {
            const msg = 'invalid Pub/Sub message format';
            console.error(`error: ${msg}`);
            res.status(400).send(`Bad Request: ${msg}`);
            return;
          }
    
          const pubSubMessage = req.body.message;
          const name = pubSubMessage.data
            ? Buffer.from(pubSubMessage.data, 'base64').toString().trim()
            : 'World';
    
          console.log(`Hello ${name}!`);
          res.status(204).send();
        });

    Python

    @app.route('/', methods=['POST'])
        def index():
            envelope = request.get_json()
            if not envelope:
                msg = 'no Pub/Sub message received'
                print(f'error: {msg}')
                return f'Bad Request: {msg}', 400
    
            if not isinstance(envelope, dict) or 'message' not in envelope:
                msg = 'invalid Pub/Sub message format'
                print(f'error: {msg}')
                return f'Bad Request: {msg}', 400
    
            pubsub_message = envelope['message']
    
            name = 'World'
            if isinstance(pubsub_message, dict) and 'data' in pubsub_message:
                name = base64.b64decode(pubsub_message['data']).decode('utf-8').strip()
    
            print(f'Hello {name}!')
    
            # Flush the stdout to avoid log buffering.
            sys.stdout.flush()
    
            return ('', 204)

    Go

    
        // PubSubMessage is the payload of a Pub/Sub event.
        type PubSubMessage struct {
        	Message struct {
        		Data []byte `json:"data,omitempty"`
        		ID   string `json:"id"`
        	} `json:"message"`
        	Subscription string `json:"subscription"`
        }
    
        // HelloPubSub receives and processes a Pub/Sub push message.
        func HelloPubSub(w http.ResponseWriter, r *http.Request) {
        	var m PubSubMessage
        	body, err := ioutil.ReadAll(r.Body)
        	if err != nil {
        		log.Printf("ioutil.ReadAll: %v", err)
        		http.Error(w, "Bad Request", http.StatusBadRequest)
        		return
        	}
        	if err := json.Unmarshal(body, &m); err != nil {
        		log.Printf("json.Unmarshal: %v", err)
        		http.Error(w, "Bad Request", http.StatusBadRequest)
        		return
        	}
    
        	name := string(m.Message.Data)
        	if name == "" {
        		name = "World"
        	}
        	log.Printf("Hello %s!", name)
        }
        

    Java

    import java.util.Base64;
        import org.apache.commons.lang3.StringUtils;
        import org.springframework.http.HttpStatus;
        import org.springframework.http.ResponseEntity;
        import org.springframework.web.bind.annotation.RequestBody;
        import org.springframework.web.bind.annotation.RequestMapping;
        import org.springframework.web.bind.annotation.RequestMethod;
        import org.springframework.web.bind.annotation.RestController;
    
        // PubsubController consumes a Pub/Sub message.
        @RestController
        public class PubSubController {
          @RequestMapping(value = "/", method = RequestMethod.POST)
          public ResponseEntity receiveMessage(@RequestBody Body body) {
            // Get PubSub message from request body.
            Body.Message message = body.getMessage();
            if (message == null) {
              String msg = "Bad Request: invalid Pub/Sub message format";
              System.out.println(msg);
              return new ResponseEntity(msg, HttpStatus.BAD_REQUEST);
            }
    
            String data = message.getData();
            String target =
                !StringUtils.isEmpty(data) ? new String(Base64.getDecoder().decode(data)) : "World";
            String msg = "Hello " + target + "!";
    
            System.out.println(msg);
            return new ResponseEntity(msg, HttpStatus.OK);
          }
        }

    Vous devez coder le service pour renvoyer un code de réponse HTTP précis. Les codes de réussite, tels que HTTP 200 ou 204, confirment le traitement complet du message Pub/Sub. Les codes d'erreur, tels que HTTP 400 ou 500, indiquent qu'une nouvelle tentative d'envoi sera effectuée, comme décrit dans le guide Recevoir des messages en mode push.

  • Un Dockerfile qui définit l'environnement d'exploitation du service. Le contenu du Dockerfile varie selon le langage.

    Node.js

    
        # Use the official lightweight Node.js 10 image.
        # https://hub.docker.com/_/node
        FROM node:10-slim
    
        # Create and change to the app directory.
        WORKDIR /usr/src/app
    
        # Copy application dependency manifests to the container image.
        # A wildcard is used to ensure both package.json AND package-lock.json are copied.
        # Copying this separately prevents re-running npm install on every code change.
        COPY package*.json ./
    
        # Install dependencies.
        # If you add a package-lock.json speed your build by switching to 'npm ci'.
        # RUN npm ci --only=production
        RUN npm install --production
    
        # Copy local code to the container image.
        COPY . .
    
        # Run the web service on container startup.
        CMD [ "npm", "start" ]
        

    Python

    
        # Use the official Python image.
        # https://hub.docker.com/_/python
        FROM python:3.8
    
        # Copy application dependency manifests to the container image.
        # Copying this separately prevents re-running pip install on every code change.
        COPY requirements.txt ./
    
        # Install production dependencies.
        RUN pip install -r requirements.txt
    
        # Copy local code to the container image.
        ENV APP_HOME /app
        WORKDIR $APP_HOME
        COPY . ./
    
        # Run the web service on container startup.
        # Use gunicorn webserver with one worker process and 8 threads.
        # For environments with multiple CPU cores, increase the number of workers
        # to be equal to the cores available.
        CMD exec gunicorn --bind :$PORT --workers 1 --threads 8 --timeout 0 main:app
        

    Go

    
        # Use the offical golang image to create a binary.
        # This is based on Debian and sets the GOPATH to /go.
        # https://hub.docker.com/_/golang
        FROM golang:1.13-buster as builder
    
        # Create and change to the app directory.
        WORKDIR /app
    
        # Retrieve application dependencies.
        # This allows the container build to reuse cached dependencies.
        # Expecting to copy go.mod and if present go.sum.
        COPY go.* ./
        RUN go mod download
    
        # Copy local code to the container image.
        COPY . ./
    
        # Build the binary.
        RUN go build -mod=readonly -v -o server
    
        # Use the official Debian slim image for a lean production container.
        # https://hub.docker.com/_/debian
        # https://docs.docker.com/develop/develop-images/multistage-build/#use-multi-stage-builds
        FROM debian:buster-slim
        RUN set -x && apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y \
            ca-certificates && \
            rm -rf /var/lib/apt/lists/*
    
        # Copy the binary to the production image from the builder stage.
        COPY --from=builder /app/server /server
    
        # Run the web service on container startup.
        CMD ["/server"]
        

    Java

    Cet exemple utilise Jib pour créer des images Docker à l'aide d'outils Java courants. Jib optimise les compilations de conteneurs sans fichier Docker ni fichier Docker installé. En savoir plus sur la compilation de conteneurs Java avec Jib.
    <plugin>
          <groupId>com.google.cloud.tools</groupId>
          <artifactId>jib-maven-plugin</artifactId>
          <version>2.1.0</version>
          <configuration>
            <to>
              <image>gcr.io/PROJECT_ID/pubsub</image>
            </to>
          </configuration>
        </plugin>
    

Pour en savoir plus sur l'authentification de l'origine des requêtes Pub/Sub, consultez la section ci-dessous Intégrer à Pub/Sub.

Transmettre le code

La transmission du code comprend trois étapes : créer une image de conteneur avec Cloud Build, importer l'image de conteneur dans Container Registry, et la déployer dans Cloud Run ou Cloud Run pour Anthos sur Google Cloud.

Pour transmettre votre code, procédez comme suit :

  1. Créez votre conteneur et publiez-le dans Container Registry :

    Node.js

        gcloud builds submit --tag gcr.io/PROJECT_ID/pubsub

    PROJECT_ID est votre ID de projet GCP et pubsub est le nom que vous souhaitez attribuer à votre service.

    En cas de réussite, un message SUCCESS apparaît contenant l'ID, l'heure de création et le nom de l'image. Celle-ci est stockée dans Container Registry et peut être réutilisée au besoin.

    Python

        gcloud builds submit --tag gcr.io/PROJECT_ID/pubsub

    PROJECT_ID est votre ID de projet GCP et pubsub est le nom que vous souhaitez attribuer à votre service.

    En cas de réussite, un message SUCCESS apparaît contenant l'ID, l'heure de création et le nom de l'image. Celle-ci est stockée dans Container Registry et peut être réutilisée au besoin.

    Go

        gcloud builds submit --tag gcr.io/PROJECT_ID/pubsub

    PROJECT_ID est votre ID de projet GCP et pubsub est le nom que vous souhaitez attribuer à votre service.

    En cas de réussite, un message SUCCESS apparaît contenant l'ID, l'heure de création et le nom de l'image. Celle-ci est stockée dans Container Registry et peut être réutilisée au besoin.

    Java

    mvn compile jib:build -Dimage=gcr.io/PROJECT_ID/pubsub

    PROJECT_ID est votre ID de projet GCP et pubsub est le nom que vous souhaitez attribuer à votre service.

    En cas de réussite, un message BUILD SUCCESS apparaît. Celle-ci est stockée dans Container Registry et peut être réutilisée si vous le souhaitez.

  2. Exécutez la commande suivante pour déployer votre application :

        gcloud run deploy pubsub-tutorial --image gcr.io/PROJECT_ID/pubsub

    Remplacez PROJECT_ID par l'ID de votre projet GCP. pubsub correspond au nom du conteneur et pubsub-tutorial au nom du service. Notez que l'image du conteneur est déployée sur le service et la région (Cloud Run) ou sur le cluster (Cloud Run pour Anthos sur Google Cloud) que vous avez précédemment configurés dans la section Configurer gcloud.

    Si vous déployez sur Cloud Run, répondez n, "Non", à l'invite "Autoriser sans authentification". En gardant le service privé, le processus d'intégration automatique de Pub/Sub de Cloud Run assure l'authentification des requêtes. Cette configuration est présentée en détail dans la section Intégrer à Pub/Sub. Pour en savoir plus sur l'authentification basée sur IAM, consultez la page Gérer l'accès.

    Patientez jusqu'à la fin du déploiement, soit environ 30 secondes. En cas de réussite, la ligne de commande affiche l'URL du service. Cette URL est utilisée pour configurer un abonnement Pub/Sub.

  3. Si vous souhaitez déployer une mise à jour de code sur le service, répétez les opérations précédentes. Chaque déploiement sur un service crée une nouvelle révision et commence automatiquement à acheminer le trafic une fois prêt.

Intégrer à Pub/Sub

Maintenant que nous avons déployé notre service Cloud Run, nous allons configurer Pub/Sub pour y transférer les messages.

Pour intégrer le service à Pub/Sub, procédez comme suit :

  1. Autorisez Pub/Sub à créer des jetons d'authentification dans votre projet :

        gcloud projects add-iam-policy-binding PROJECT_ID \
             --member=serviceAccount:service-PROJECT-NUMBER@gcp-sa-pubsub.iam.gserviceaccount.com \
             --role=roles/iam.serviceAccountTokenCreator

    Remplacer

    • PROJECT-ID par l'ID de votre projet Google Cloud.
    • PROJECT-NUMBER par le numéro de votre projet Google Cloud.
  2. Créez ou sélectionnez un compte de service pour représenter l'identité de l'abonnement Pub/Sub.

        gcloud iam service-accounts create cloud-run-pubsub-invoker \
             --display-name "Cloud Run Pub/Sub Invoker"

    Vous pouvez utiliser cloud-run-pubsub-invoker ou le remplacer par un nom unique dans votre projet Google Cloud.

  3. Créez un abonnement Pub/Sub avec le compte de service à l'aide de l'onglet approprié, selon que vous utilisez Cloud Run ou Cloud Run pour Anthos sur Google Cloud.

    Cloud Run

    1. Autorisez le compte de service demandeur à appeler votre service pubsub-tutorial :

          gcloud run services add-iam-policy-binding pubsub-tutorial \
             --member=serviceAccount:cloud-run-pubsub-invoker@PROJECT_ID.iam.gserviceaccount.com \
             --role=roles/run.invoker

      La diffusion des modifications IAM peut prendre plusieurs minutes. Pendant ce temps, des erreurs HTTP 403 peuvent s'afficher dans les journaux de service.

    2. Créez un abonnement Pub/Sub avec le compte de service :

          gcloud pubsub subscriptions create myRunSubscription --topic myRunTopic \
             --push-endpoint=SERVICE-URL/ \
             --push-auth-service-account=cloud-run-pubsub-invoker@PROJECT_ID.iam.gserviceaccount.com

      Remplacer

      • myRunTopic par le sujet que vous avez créé précédemment.
      • SERVICE-URL par l'URL HTTPS fournie lors du déploiement du service. Cette URL fonctionne même si vous avez également ajouté un mappage de domaine.
      • PROJECT-ID par l'ID de votre projet Google Cloud.

      L'option --push-auth-service-account active la fonctionnalité push de Pub/Sub pour l'authentification et l'autorisation.

      Votre domaine de service Cloud Run est automatiquement enregistré pour une utilisation avec les abonnements Pub/Sub.

      Pour Cloud Run uniquement, il existe une vérification d'authentification intégrée pour confirmer la validité du jeton et une autorisation pour vérifier que le compte de service est autorisé à appeler le service Cloud Run.

    Cloud Run sur GKE

    1. Modifiez le domaine par défaut de votre cluster ou ajoutez un mappage de domaine à votre service.

    2. Enregistrez la propriété du domaine pour Pub/Sub.

    3. Activez le protocole HTTPS de votre cluster.

    4. Ajoutez un code pour valider le jeton d'authentification associé aux messages Pub/Sub. Un exemple de code est indiqué dans la section Authentification et autorisation par le point de terminaison push.

    L'authentification doit s'assurer que le jeton est valide et associé au compte de service attendu. Contrairement à Cloud Run (entièrement géré), Cloud Run pour Anthos sur Google Cloud ne dispose d'aucune vérification intégrée permettant de confirmer la validité du jeton ou qu'il est autorisé à invoquer Cloud Run pour Anthos sur le service Google Cloud.

    1. Créez un abonnement Pub/Sub avec le compte de service :

          gcloud pubsub subscriptions create myRunSubscription --topic myRunTopic \
               --push-endpoint=SERVICE-URL/ \
               --push-auth-service-account=cloud-run-pubsub-invoker@PROJECT_ID.iam.gserviceaccount.com

      Remplacer

      • myRunTopic par le sujet que vous avez créé précédemment.
      • SERVICE-URL avec l'URL de votre service personnalisé. Spécifiez https comme protocole.
      • PROJECT-ID par l'ID de votre projet Google Cloud.

      L'option --push-auth-service-account active la fonctionnalité push de Pub/Sub pour l'authentification et l'autorisation.

Votre service est maintenant entièrement intégré à Pub/Sub.

Essayez-le !

Pour tester la solution de bout en bout, procédez comme suit :

  1. Envoyez un message au sujet sur Pub/Sub :

        gcloud pubsub topics publish myRunTopic --message "Runner"

    Au lieu d'utiliser la ligne de commande comme décrit dans ce tutoriel, vous pouvez programmer la publication des messages. Reportez-vous à la page Publier des messages pour en savoir plus.

  2. Accédez aux journaux du service :

    1. Accédez à Google Cloud Console.
    2. Cliquez sur le service pubsub-tutorial.
    3. Sélectionnez l'onglet Journaux.

      L'affichage des journaux peut nécessiter quelques instants. S'ils n'apparaissent pas immédiatement, patientez et vérifiez de nouveau.

  3. Recherchez le message "Hello Runner!".

Nettoyer

Pour mieux comprendre comment utiliser Cloud Run avec Pub/Sub, ignorez le nettoyage pour le moment et poursuivez avec le tutoriel Traiter des images avec Cloud Run.

Si vous avez créé un projet pour ce tutoriel, supprimez-le. Si vous avez utilisé un projet existant et que vous souhaitez le conserver sans apporter de modifications dans ce tutoriel, supprimez les ressources créées pour ce tutoriel.

Supprimer le projet

Le moyen le plus simple d'empêcher la facturation est de supprimer le projet que vous avez créé pour ce tutoriel.

Pour supprimer le projet :

  1. Dans Cloud Console, accédez à la page Gérer les ressources.

    Accéder à la page Gérer les ressources

  2. Dans la liste des projets, sélectionnez le projet que vous souhaitez supprimer, puis cliquez sur Supprimer.
  3. Dans la boîte de dialogue, saisissez l'ID du projet, puis cliquez sur Arrêter pour supprimer le projet.

Supprimer des ressources du tutoriel

  1. Supprimez le service Cloud Run que vous avez déployé dans ce tutoriel :

        gcloud run services delete SERVICE-NAME

    SERVICE-NAME est le nom du service que vous avez choisi.

    Vous pouvez également supprimer des services Cloud Run à partir de Google Cloud Console.

  2. Supprimez les configurations gcloud par défaut que vous avez ajoutées lors de la configuration du tutoriel.

    Si vous utilisez Cloud Run (entièrement géré), supprimez le paramètre de région :

     gcloud config unset run/region
        

    Si vous utilisez Cloud Run pour Anthos sur Google Cloud, supprimez la configuration du cluster :

     gcloud config unset run/cluster run/cluster
         gcloud config unset run/cluster run/cluster_location
        
  3. Supprimez la configuration du projet :

     gcloud config unset project
        
  4. Supprimez les autres ressources Google Cloud créées dans ce tutoriel :

Étape suivante