Tutoriel : Dépannage local d'un service Cloud Run


Ce tutoriel explique comment un développeur de services peut résoudre un problème de service Cloud Run à l'aide des outils de Google Cloud Observability pour la découverte et d'un workflow de développement local pour l'enquête.

Fournie en complément du guide de dépannage, cette étude de cas détaillée utilise un exemple de projet qui génère des erreurs d'exécution lors du déploiement et que vous devez dépanner de manière à trouver et résoudre le problème.

Objectifs

  • Écrire, créer et déployer un service sur Cloud Run
  • Exploiter Error Reporting et Cloud Logging pour identifier une erreur
  • Récupérer l'image de conteneur à partir de Container Registry pour une analyse de la cause fondamentale
  • Corriger le service de production, puis l'améliorer pour atténuer les problèmes futurs

Coûts

Dans ce document, vous utilisez les composants facturables suivants de Google Cloud :

Obtenez une estimation des coûts en fonction de votre utilisation prévue à l'aide du simulateur de coût. Les nouveaux utilisateurs de Google Cloud peuvent bénéficier d'un essai gratuit.

Avant de commencer

  1. Sign in to your Google Cloud account. If you're new to Google Cloud, create an account to evaluate how our products perform in real-world scenarios. New customers also get $300 in free credits to run, test, and deploy workloads.
  2. In the Google Cloud console, on the project selector page, select or create a Google Cloud project.

    Go to project selector

  3. Make sure that billing is enabled for your Google Cloud project.

  4. In the Google Cloud console, on the project selector page, select or create a Google Cloud project.

    Go to project selector

  5. Make sure that billing is enabled for your Google Cloud project.

  6. Activez l'API Cloud Run Admin
  7. Installez et initialisez gcloud CLI.
  8. Mettez à jour les composants :
    gcloud components update
  9. Suivez les instructions pour installer Docker localement.

Rôles requis

Pour obtenir les autorisations nécessaires pour suivre le tutoriel, demandez à votre administrateur de vous accorder les rôles IAM suivants sur votre projet :

Pour en savoir plus sur l'attribution de rôles, consultez la page Gérer l'accès aux projets, aux dossiers et aux organisations.

Vous pouvez également obtenir les autorisations requises via des rôles personnalisés ou d'autres rôles prédéfinis.

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. 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.

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 matière 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 tenir compte de l'emplacement des autres produits Google Cloud utilisés par votre service Cloud Run. L'utilisation conjointe de produits Google Cloud dans plusieurs emplacements peut avoir une incidence sur la latence et le coût de votre service.

Cloud Run est disponible dans les régions suivantes :

Soumis aux tarifs de niveau 1

Soumis aux tarifs de niveau 2

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

Assembler le code

Créez un service greeter Cloud Run étape par étape. Nous vous rappelons que ce service crée une erreur d'exécution intentionnellement pour l'exercice de dépannage.

  1. Créez un projet :

    Node.js

    Créez un projet Node.js en définissant le package de service, les dépendances initiales et certaines opérations courantes.

    1. Créez un répertoire hello-service :

      mkdir hello-service
      cd hello-service
      
    2. Créez un projet Node.js en générant un fichier package.json :

      npm init --yes
      npm install --save express@4
      
    3. Ouvrez le nouveau fichier package.json dans votre éditeur et configurez un script start pour exécuter node index.js. Lorsque vous avez terminé, le fichier se présente comme suit :

      {
        "name": "hello-broken",
        "description": "Broken Cloud Run service for troubleshooting practice",
        "version": "1.0.0",
        "private": true,
        "main": "index.js",
        "scripts": {
          "start": "node index.js",
          "test": "echo \"Error: no test specified\" && exit 0",
          "system-test": "NAME=Cloud c8 mocha -p -j 2 test/system.test.js --timeout=360000 --exit"
        },
        "engines": {
          "node": ">=16.0.0"
        },
        "author": "Google LLC",
        "license": "Apache-2.0",
        "dependencies": {
          "express": "^4.17.1"
        },
        "devDependencies": {
          "c8": "^10.0.0",
          "google-auth-library": "^9.0.0",
          "got": "^11.5.0",
          "mocha": "^10.0.0"
        }
      }
      

    Si vous continuez de faire évoluer ce service au-delà du présent tutoriel, pensez à renseigner la description et l'auteur, puis à évaluer la licence. Pour en savoir plus, consultez la documentation de package.json.

    Python

    1. Créez un répertoire hello-service :

      mkdir hello-service
      cd hello-service
      
    2. Créez un fichier requirements.txt et copiez-y vos dépendances :

      Flask==3.0.3
      pytest==8.2.0; python_version > "3.0"
      # pin pytest to 4.6.11 for Python2.
      pytest==4.6.11; python_version < "3.0"
      gunicorn==22.0.0
      Werkzeug==3.0.3
      

    Go

    1. Créez un répertoire hello-service :

      mkdir hello-service
      cd hello-service
      
    2. Créez un projet Go en initialisant un nouveau module Go :

      go mod init example.com/hello-service
      

    Vous pouvez mettre à jour le nom si vous le souhaitez : cette opération est conseillée si le code est publié dans un dépôt de code accessible sur le Web.

    Java

    1. Créez un projet Maven :

      mvn archetype:generate \
        -DgroupId=com.example.cloudrun \
        -DartifactId=hello-service \
        -DarchetypeArtifactId=maven-archetype-quickstart \
        -DinteractiveMode=false
      
    2. Copiez les dépendances dans votre liste de dépendances pom.xml (entre les éléments <dependencies>) :

      <dependency>
        <groupId>com.sparkjava</groupId>
        <artifactId>spark-core</artifactId>
        <version>2.9.4</version>
      </dependency>
      <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-api</artifactId>
        <version>2.0.12</version>
      </dependency>
      <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-simple</artifactId>
        <version>2.0.12</version>
      </dependency>
      
    3. Copiez le paramètre de création dans votre fichier pom.xml (sous les éléments <dependencies>) :

      <build>
        <plugins>
          <plugin>
            <groupId>com.google.cloud.tools</groupId>
            <artifactId>jib-maven-plugin</artifactId>
            <version>3.4.0</version>
            <configuration>
              <to>
                <image>gcr.io/PROJECT_ID/hello-service</image>
              </to>
            </configuration>
          </plugin>
        </plugins>
      </build>
      

  2. Créez un service HTTP pour gérer les requêtes entrantes :

    Node.js

    const express = require('express');
    const app = express();
    
    app.get('/', (req, res) => {
      console.log('hello: received request.');
    
      const {NAME} = process.env;
      if (!NAME) {
        // Plain error logs do not appear in Stackdriver Error Reporting.
        console.error('Environment validation failed.');
        console.error(new Error('Missing required server parameter'));
        return res.status(500).send('Internal Server Error');
      }
      res.send(`Hello ${NAME}!`);
    });
    const port = parseInt(process.env.PORT) || 8080;
    app.listen(port, () => {
      console.log(`hello: listening on port ${port}`);
    });

    Python

    import json
    import os
    
    from flask import Flask
    
    
    app = Flask(__name__)
    
    
    @app.route("/", methods=["GET"])
    def index():
        """Example route for testing local troubleshooting.
    
        This route may raise an HTTP 5XX error due to missing environment variable.
        """
        print("hello: received request.")
    
        NAME = os.getenv("NAME")
    
        if not NAME:
            print("Environment validation failed.")
            raise Exception("Missing required service parameter.")
    
        return f"Hello {NAME}"
    
    
    if __name__ == "__main__":
        PORT = int(os.getenv("PORT")) if os.getenv("PORT") else 8080
    
        # This is used when running locally. Gunicorn is used to run the
        # application on Cloud Run. See entrypoint in Dockerfile.
        app.run(host="127.0.0.1", port=PORT, debug=True)

    Go

    
    // Sample hello demonstrates a difficult to troubleshoot service.
    package main
    
    import (
    	"fmt"
    	"log"
    	"net/http"
    	"os"
    )
    
    func main() {
    	log.Print("hello: service started")
    
    	http.HandleFunc("/", helloHandler)
    
    
    	port := os.Getenv("PORT")
    	if port == "" {
    		port = "8080"
    		log.Printf("Defaulting to port %s", port)
    	}
    
    	log.Printf("Listening on port %s", port)
    	log.Fatal(http.ListenAndServe(fmt.Sprintf(":%s", port), nil))
    }
    
    func helloHandler(w http.ResponseWriter, r *http.Request) {
    	log.Print("hello: received request")
    
    	name := os.Getenv("NAME")
    	if name == "" {
    		log.Printf("Missing required server parameter")
    		// The panic stack trace appears in Cloud Error Reporting.
    		panic("Missing required server parameter")
    	}
    
    	fmt.Fprintf(w, "Hello %s!\n", name)
    }
    

    Java

    import static spark.Spark.get;
    import static spark.Spark.port;
    
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    
    public class App {
    
      private static final Logger logger = LoggerFactory.getLogger(App.class);
    
      public static void main(String[] args) {
        int port = Integer.parseInt(System.getenv().getOrDefault("PORT", "8080"));
        port(port);
    
        get(
            "/",
            (req, res) -> {
              logger.info("Hello: received request.");
              String name = System.getenv("NAME");
              if (name == null) {
                // Standard error logs do not appear in Stackdriver Error Reporting.
                System.err.println("Environment validation failed.");
                String msg = "Missing required server parameter";
                logger.error(msg, new Exception(msg));
                res.status(500);
                return "Internal Server Error";
              }
              res.status(200);
              return String.format("Hello %s!", name);
            });
      }
    }

  3. Créez un fichier Dockerfile pour définir l'image de conteneur utilisée pour déployer le service :

    Node.js

    
    # Use the official lightweight Node.js image.
    # https://hub.docker.com/_/node
    FROM node:20-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 copying both package.json AND package-lock.json (when available).
    # Copying this first prevents re-running npm install on every code change.
    COPY package*.json ./
    
    # Install dependencies.
    # if you need a deterministic and repeatable build create a
    # package-lock.json file and use npm ci:
    # RUN npm ci --omit=dev
    # if you need to include development dependencies during development
    # of your application, use:
    # RUN npm install --dev
    
    RUN npm install --omit=dev
    
    # 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.11
    
    # Allow statements and log messages to immediately appear in the Cloud Run logs
    ENV PYTHONUNBUFFERED True
    
    # 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.
    # Timeout is set to 0 to disable the timeouts of the workers to allow Cloud Run to handle instance scaling.
    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.21-bookworm 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 -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:bookworm-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 builds de conteneurs sans requérir de fichier Dockerfile ni d'installation Docker. Découvrez comment créer des conteneurs Java avec Jib.

    <plugin>
      <groupId>com.google.cloud.tools</groupId>
      <artifactId>jib-maven-plugin</artifactId>
      <version>3.4.0</version>
      <configuration>
        <to>
          <image>gcr.io/PROJECT_ID/hello-service</image>
        </to>
      </configuration>
    </plugin>
    

Transmettre le code

La transmission du code se déroule en trois étapes : création d'une image de conteneur avec Cloud Build, importation de l'image de conteneur dans Container Registry, puis déploiement de cette image dans Cloud Run.

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/hello-service

    PROJECT_ID correspond à l’ID de votre projet Google Cloud. Vous pouvez vérifier l'ID de votre projet actuel avec gcloud config get-value project.

    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/hello-service

    PROJECT_ID correspond à l’ID de votre projet Google Cloud. Vous pouvez vérifier l'ID de votre projet actuel avec gcloud config get-value project.

    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/hello-service

    PROJECT_ID correspond à l’ID de votre projet Google Cloud. Vous pouvez vérifier l'ID de votre projet actuel avec gcloud config get-value project.

    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

    1. Utilisez l'assistant d'identification gcloud pour autoriser Docker à transférer du contenu vers votre registre de conteneurs.
      gcloud auth configure-docker
    2. Utilisez le plug-in Maven Jib pour créer et transférer le conteneur dans Container Registry.
      mvn compile jib:build -Dimage=gcr.io/PROJECT_ID/hello-service

    PROJECT_ID correspond à l’ID de votre projet Google Cloud. Vous pouvez vérifier l'ID de votre projet actuel avec gcloud config get-value project.

    En cas de réussite, un message BUILD SUCCESS apparaît. L'image 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 hello-service --image gcr.io/PROJECT_ID/hello-service

    Remplacez PROJECT_ID par l'ID de votre projet Google Cloud. hello-service correspond à la fois au nom de l'image de conteneur et au nom du service Cloud Run. Notez que l'image de conteneur est déployée sur le service et la région que vous avez précédemment configurés dans la section Configurer gcloud.

    Répondez y, "oui", à l'invite allow unauthenticated (autoriser sans authentification). Pour en savoir plus sur l'authentification basée sur IAM, consultez la page Gérer les 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.

Essayez-le !

Essayez le service pour vérifier que vous l'avez bien déployé. Les requêtes doivent échouer avec une erreur HTTP 500 ou 503 (membres de la classe d'erreurs serveur 5xx). Le tutoriel vous explique comment résoudre cette erreur.

Une URL navigable est automatiquement attribuée au service.

  1. Accédez à cette URL avec votre navigateur Web :

    1. Ouvrez un navigateur Web.

    2. Recherchez l'URL de service générée par la commande de déploiement précédente.

      Si la commande de déploiement n'a pas fourni d'URL, une erreur s'est produite. Examinez le message d'erreur et agissez en conséquence : en l'absence de conseils exploitables, consultez le guide de dépannage et réessayez d'utiliser la commande de déploiement.

    3. Accédez à cette URL en la copiant dans la barre d'adresse de votre navigateur, puis en appuyant sur ENTER.

  2. Consultez l'erreur HTTP 500 ou HTTP 503.

    Si vous recevez une erreur HTTP 403, vous avez peut-être refusé allow unauthenticated invocations à l'invite de déploiement. Accordez un accès non authentifié au service pour résoudre ce problème :

    gcloud run services add-iam-policy-binding hello-service \
      --member="allUsers" \
      --role="roles/run.invoker"
    

Pour en savoir plus, consultez la section Autoriser l'accès public (non authentifié).

Examiner le problème

Vérifiez si l'erreur HTTP 5xx rencontrée ci-dessus dans Essayez-le ! s'est produite en tant qu'erreur d'exécution de production. Ce tutoriel vous guide à travers un processus formel de gestion de cette erreur. Bien que les processus de résolution des erreurs de production varient considérablement, ce tutoriel présente une séquence particulière d'étapes pour montrer l'application d'outils et de techniques utiles.

Pour examiner ce problème, procédez comme suit :

  • Rassemblez plus de détails sur l'erreur signalée afin d'effectuer une analyse approfondie et de définir une stratégie d'atténuation.
  • Pour réduire les répercussions sur l'utilisateur, vous pouvez décider d'appliquer une correction ou un rollback à une version connue et opérationnelle.
  • Reproduisez l'erreur pour vérifier que les informations correctes ont bien été collectées et que l'erreur n'est pas ponctuelle.
  • Effectuez une analyse des causes fondamentales du bug pour trouver le code, la configuration ou le processus à l'origine de cette erreur.

Au début de l'enquête, vous avez une URL, un horodatage et un message "Erreur de serveur interne".

Collecter des informations supplémentaires

Rassemblez plus d'informations sur le problème pour comprendre ce qui s'est passé et déterminer la marche à suivre.

Utilisez les outils Google Cloud Observability disponibles pour collecter plus de détails :

  1. Utilisez la console Error Reporting, qui fournit un tableau de bord avec des détails et un suivi récurrent des erreurs avec une trace de la pile reconnue.

    Accéder à la console Error Reporting

    Capture d&#39;écran de la liste des erreurs, y compris les colonnes &quot;État de résolution&quot;, &quot;Occurrences&quot;, &quot;Erreur&quot; et &quot;Emplacement de l&#39;erreur&quot;.
    Liste des erreurs enregistrées. Les erreurs sont regroupées par message selon les versions, les services et les plates-formes.
  2. Cliquez sur l'erreur pour afficher les détails des traces de pile, en notant les appels de fonction effectués juste avant l'erreur.

    Capture d&#39;écran d&#39;une seule trace de la pile analysée, montrant un profil commun de cette erreur.
    L'exemple de trace de la pile sur la page "Détails" de l'erreur affiche une seule instance de l'erreur. Vous pouvez examiner les instances individuelles.
  3. Utilisez Cloud Logging pour examiner la séquence d'opérations menant au problème, y compris les messages d'erreur qui ne sont pas inclus dans la console Error Reporting en raison de l'absence de trace de pile d'erreur reconnue :

    Accéder à la console Cloud Logging

    Sélectionnez Révision dans Cloud Run > hello-service dans la première boîte déroulante. Les entrées de journal générées par votre service seront alors filtrées.

Renseignez-vous sur l'affichage des journaux dans Cloud Run.

Procéder au rollback vers la version correcte

S'il s'agit d'un service établi, connu pour fonctionner, une révision précédente du service sera présente sur Cloud Run. Dans ce tutoriel, nous utilisons un nouveau service sans version précédente. Vous ne pouvez donc pas effectuer de rollback.

Toutefois, si vous disposez d'un service avec des versions précédentes à partir desquelles vous pouvez procéder à un rollback, consultez la section Afficher les détails d'une révision pour extraire le nom du conteneur et les détails de configuration nécessaires pour créer un déploiement opérationnel de votre service.

Reproduire l'erreur

En utilisant les détails que vous avez obtenus précédemment, vérifiez que le problème se produit régulièrement dans les conditions de test.

Envoyez la même requête HTTP en essayant à nouveau le service, et vérifiez si la même erreur et les mêmes détails sont signalés. L'affichage des détails de l'erreur peut prendre un certain temps.

Étant donné que l'exemple de service de ce tutoriel est en lecture seule et ne déclenche aucun effet secondaire compliqué, la reproduction des erreurs en production est sécurisée. Toutefois, pour de nombreux services réels, ce ne sera pas le cas : vous devrez peut-être reproduire les erreurs dans un environnement de test ou limiter cette étape à une enquête locale.

La reproduction de l'erreur permet d'établir le contexte pour la poursuite des travaux. Par exemple, si les développeurs ne peuvent pas reproduire l'erreur, une analyse plus approfondie peut nécessiter une instrumentation supplémentaire du service.

Effectuer une analyse des causes fondamentales

L'analyse des causes fondamentales est une étape importante d'un dépannage efficace pour vous permettre de résoudre le problème plutôt qu'un symptôme.

Auparavant, dans ce tutoriel, vous avez reproduit le problème sur Cloud Run, ce qui confirme que le problème se manifeste lorsque le service est hébergé sur Cloud Run. À présent, reproduisez le problème localement pour déterminer s'il résulte du code ou s'il apparaît uniquement dans l'hébergement de production.

  1. Si vous n'avez pas utilisé l'interface de ligne de commande Docker en local avec Container Registry, authentifiez-la avec gcloud :

    gcloud auth configure-docker

    Pour découvrir d'autres approches, consultez la page Méthodes d'authentification de Container Registry.

  2. Si le dernier nom d'image de conteneur utilisé n'est pas disponible, la description du service contient les informations de l'image de conteneur la plus récemment déployée :

    gcloud run services describe hello-service

    Recherchez le nom de l'image de conteneur dans l'objet spec. Une commande plus ciblée peut le récupérer directement :

    gcloud run services describe hello-service \
       --format="value(spec.template.spec.containers.image)"

    Cette commande indique un nom d'image de conteneur tel que gcr.io/PROJECT_ID/hello-service.

  3. Extrayez l'image de conteneur à partir de Container Registry vers votre environnement. Cette opération peut prendre plusieurs minutes lors du téléchargement de l'image de conteneur :

    docker pull gcr.io/PROJECT_ID/hello-service

    Les mises à jour ultérieures de l'image de conteneur qui réutilisent ce nom peuvent être récupérées avec la même commande. Si vous ignorez cette étape, la commande docker run ci-dessous extrait une image de conteneur si celle-ci n'est pas présente sur l'ordinateur local.

  4. Exécutez le service localement pour vérifier que le problème n'est pas propre à Cloud Run :

    PORT=8080 && docker run --rm -e PORT=$PORT -p 9000:$PORT \
       gcr.io/PROJECT_ID/hello-service

    Voici le détail des éléments de la commande ci-dessus :

    • La variable d'environnement PORT permet au service de déterminer le port d'écoute au sein du conteneur.
    • La commande run démarre le conteneur, affichant par défaut la commande "entrypoint" définie dans le fichier Dockerfile ou une image de conteneur parent.
    • L'option --rm supprime l'instance de conteneur en cas de fermeture.
    • L'option -e attribue une valeur à une variable d'environnement. -e PORT=$PORT propage la variable PORT du système local dans le conteneur avec le même nom de variable.
    • L'option -p publie le conteneur en tant que service disponible sur localhost sur le port 9000. Les requêtes vers localhost:9000 seront acheminées vers le conteneur sur le port 8080. Cela signifie que le numéro de port de sortie utilisé ne correspondra pas au numéro de port permettant d'accéder au service.
    • L'argument final gcr.io/PROJECT_ID/hello-service est une image de conteneur tag, un libellé lisible pour l'identifiant de hachage sha256 d'une image de conteneur. Si elle n'est pas disponible localement, Docker tente de récupérer l'image à partir d'un registre distant.

    Dans votre navigateur, ouvrez http://localhost:9000. Vérifiez la sortie du terminal en cas de messages d'erreur qui correspondent à ceux de {ops_name}}.

    Si le problème ne peut être reproduit localement, il peut être unique à l'environnement Cloud Run. Consultez le guide de dépannage de Cloud Run pour connaître les domaines spécifiques à examiner.

    Dans ce cas, l'erreur est reproduite localement.

Maintenant que l'erreur est confirmée par deux fois comme étant persistante et causée par le code de service plutôt que par la plate-forme d'hébergement, il est temps d'examiner le code de plus près.

Pour les besoins de ce tutoriel, vous pouvez supposer que le code du conteneur et le code du système local sont identiques.

Revoyez la trace de pile du rapport d'erreurs et la référence croisée avec le code pour trouver les lignes spécifiques qui sont responsables du problème.

Node.js

Recherchez la source du message d'erreur dans le fichier index.js autour du numéro de ligne indiqué dans la trace de pile affichée dans les journaux :
const {NAME} = process.env;
if (!NAME) {
  // Plain error logs do not appear in Stackdriver Error Reporting.
  console.error('Environment validation failed.');
  console.error(new Error('Missing required server parameter'));
  return res.status(500).send('Internal Server Error');
}

Python

Recherchez la source du message d'erreur dans le fichier main.py autour du numéro de ligne indiqué dans la trace de pile affichée dans les journaux :
NAME = os.getenv("NAME")

if not NAME:
    print("Environment validation failed.")
    raise Exception("Missing required service parameter.")

Go

Recherchez la source du message d'erreur dans le fichier main.go autour du numéro de ligne indiqué dans la trace de pile affichée dans les journaux :

name := os.Getenv("NAME")
if name == "" {
	log.Printf("Missing required server parameter")
	// The panic stack trace appears in Cloud Error Reporting.
	panic("Missing required server parameter")
}

Java

Recherchez la source du message d'erreur dans le fichier App.java autour du numéro de ligne indiqué dans la trace de pile affichée dans les journaux :

String name = System.getenv("NAME");
if (name == null) {
  // Standard error logs do not appear in Stackdriver Error Reporting.
  System.err.println("Environment validation failed.");
  String msg = "Missing required server parameter";
  logger.error(msg, new Exception(msg));
  res.status(500);
  return "Internal Server Error";
}

En examinant le code, on remarque que les actions suivantes sont effectuées alors que la variable d'environnement NAME n'est pas définie :

  • Une erreur est consignée dans Google Cloud Observability.
  • Une réponse d'erreur HTTP est envoyée.

Le problème est causé par une variable manquante, mais la cause fondamentale est plus spécifique : la modification du code qui a créé une dépendance forte envers une variable d'environnement n'incluait pas les modifications liées aux scripts de déploiement et à la documentation concernant les exigences d'exécution.

Corriger la cause fondamentale

Maintenant que nous avons collecté le code et identifié la cause fondamentale potentielle, nous pouvons prendre des mesures pour la corriger.

  • Vérifiez que le service fonctionne localement avec l'environnement NAME disponible sur place :

    1. Exécutez le conteneur localement avec la variable d'environnement ajoutée :

      PORT=8080 && docker run --rm -e PORT=$PORT -p 9000:$PORT \
       -e NAME="Local World!" \
       gcr.io/PROJECT_ID/hello-service
    2. Accédez à http://localhost:9000 dans votre navigateur.

    3. "Hello Local World !" apparaît sur la page.

  • Modifiez l'environnement de service Cloud Run en cours d'exécution pour inclure cette variable :

    1. Exécutez la commande de mise à jour des services pour ajouter une variable d'environnement :

      gcloud run services update hello-service \
        --set-env-vars NAME=Override
      
    2. Patientez quelques secondes pendant que Cloud Run crée une révision basée sur la révision précédente avec la nouvelle variable d'environnement ajoutée.

  • Vérifiez que le problème de service est maintenant corrigé :

    1. Accédez à l'URL du service Cloud Run dans votre navigateur.
    2. "Hello Override !" apparaît sur la page.
    3. Vérifiez qu'aucune erreur ni aucun message inattendu n'apparaît dans Cloud Logging ou Error Reporting.

Accélérer les prochains dépannages

Dans cet exemple de problème de production, l'erreur était liée à la configuration opérationnelle. Des modifications sont apportées au code afin de minimiser l'impact de ce problème à l'avenir.

  • Améliorez le journal d'erreurs pour inclure des détails plus spécifiques.
  • Au lieu de renvoyer une erreur, demandez au service de revenir à une valeur par défaut sûre. Si l'utilisation d'une valeur par défaut implique une modification de la fonctionnalité normale, utilisez un message d'avertissement à des fins de surveillance.

Passons maintenant à la suppression d'une dépendance forte envers la variable d'environnement NAME.

  1. Supprimez le code de gestion NAME existant :

    Node.js

    const {NAME} = process.env;
    if (!NAME) {
      // Plain error logs do not appear in Stackdriver Error Reporting.
      console.error('Environment validation failed.');
      console.error(new Error('Missing required server parameter'));
      return res.status(500).send('Internal Server Error');
    }

    Python

    NAME = os.getenv("NAME")
    
    if not NAME:
        print("Environment validation failed.")
        raise Exception("Missing required service parameter.")

    Go

    name := os.Getenv("NAME")
    if name == "" {
    	log.Printf("Missing required server parameter")
    	// The panic stack trace appears in Cloud Error Reporting.
    	panic("Missing required server parameter")
    }

    Java

    String name = System.getenv("NAME");
    if (name == null) {
      // Standard error logs do not appear in Stackdriver Error Reporting.
      System.err.println("Environment validation failed.");
      String msg = "Missing required server parameter";
      logger.error(msg, new Exception(msg));
      res.status(500);
      return "Internal Server Error";
    }

  2. Ajoutez du code définissant une valeur de remplacement :

    Node.js

    const NAME = process.env.NAME || 'World';
    if (!process.env.NAME) {
      console.log(
        JSON.stringify({
          severity: 'WARNING',
          message: `NAME not set, default to '${NAME}'`,
        })
      );
    }

    Python

    NAME = os.getenv("NAME")
    
    if not NAME:
        NAME = "World"
        error_message = {
            "severity": "WARNING",
            "message": f"NAME not set, default to {NAME}",
        }
        print(json.dumps(error_message))

    Go

    name := os.Getenv("NAME")
    if name == "" {
    	name = "World"
    	log.Printf("warning: NAME not set, default to %s", name)
    }

    Java

    String name = System.getenv().getOrDefault("NAME", "World");
    if (System.getenv("NAME") == null) {
      logger.warn(String.format("NAME not set, default to %s", name));
    }

  3. Effectuez un test local en recréant et en exécutant le conteneur dans les cas de configuration concernés :

    Node.js

    docker build --tag gcr.io/PROJECT_ID/hello-service .

    Python

    docker build --tag gcr.io/PROJECT_ID/hello-service .

    Go

    docker build --tag gcr.io/PROJECT_ID/hello-service .

    Java

    mvn compile jib:build

    Vérifiez que la variable d'environnement NAME fonctionne toujours :

    PORT=8080 && docker run --rm -e PORT=$PORT -p 9000:$PORT \
     -e NAME="Robust World" \
     gcr.io/PROJECT_ID/hello-service

    Vérifiez que le service fonctionne sans la variable NAME :

    PORT=8080 && docker run --rm -e PORT=$PORT -p 9000:$PORT \
     gcr.io/PROJECT_ID/hello-service

    Si le service ne renvoie pas de résultat, vérifiez que vous n'avez pas supprimé de lignes supplémentaires lors de la suppression de code de la première étape, comme par exemple les lignes utilisées pour rédiger la réponse.

  4. Déployez le code en consultant à nouveau la section Déployer votre code.

    À chaque déploiement sur le service, une révision est créée et le trafic commence automatiquement à être diffusé une fois le déploiement prêt.

    Pour effacer les variables d'environnement définies précédemment, exécutez la commande suivante :

    gcloud run services update hello-service --clear-env-vars

Ajoutez la nouvelle fonctionnalité de valeur par défaut au champ d'application des tests automatisés pour le service.

Rechercher d'autres problèmes dans les journaux

D'autres problèmes peuvent survenir dans la visionneuse de journaux de ce service. Par exemple, un appel système non compatible apparaîtra dans les journaux comme une "limitation du bac à sable de conteneur".

Par exemple, les services Node.js génèrent parfois ce message de journal :

Container Sandbox Limitation: Unsupported syscall statx(0xffffff9c,0x3e1ba8e86d88,0x0,0xfff,0x3e1ba8e86970,0x3e1ba8e86a90). Please, refer to https://gvisor.dev/c/linux/amd64/statx for more information.

Dans ce cas, la non-compatibilité n'a aucune incidence sur l'exemple de service "hello-service".

Dépannage de Terraform

Pour le dépannage ou les questions liés à Terraform, consultez la page Dépannage des problèmes de validation des règles Terraform ou contactez l'assistance Terraform.

Effectuer un nettoyage

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 les modifications du présent 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. In the Google Cloud console, go to the Manage resources page.

    Go to Manage resources

  2. In the project list, select the project that you want to delete, and then click Delete.
  3. In the dialog, type the project ID, and then click Shut down to delete the project.

Supprimer les 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 de service que vous avez choisi.

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

  2. Supprimez la configuration régionale gcloud par défaut que vous avez ajoutée lors de la configuration du tutoriel :

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

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

Étape suivante