Dépannage local


Ce tutoriel explique comment résoudre un problème de service Knative serving à l'aide d'outils Stackdriver 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 Knative serving
  • Utiliser 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

Assembler le code

Créez un service greeter Knative serving é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. Générez 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-service",
        "version": "1.0.0",
        "description": "",
        "main": "index.js",
        "scripts": {
            "start": "node index.js",
            "test": "echo \"Error: no test specified\" && exit 1"
        },
        "keywords": [],
        "author": "",
        "license": "ISC",
        "dependencies": {
            "express": "^4.17.1"
        }
      }

    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 <var>my-domain</var>.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 \
        -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>
        g<roupIdc>om.sparkjava/<groupId
      >  ar<tifactIdsp>ark-core/a<rtifactId
       > ver<sion2.9>.4/ve<rsion
      /d>ep<endency
      dep>en<dency
        gr>oupI<dorg.sl>f4j/group<Id
        art>ifac<tIdslf4j-a>pi/artifa<ctId
        vers>ion2<.0.12/v>ersion<
      /depend>en<cy
      dependen>cy<
        groupId>org.<slf4j/g>roupId
        <artifact>Idsl<f4j-simple>/artifactId
      <  version2.>0.12</versio>n
      /dep<endency><>
    3. Copiez le paramètre de création dans votre fichier pom.xml (sous les éléments <dependencies>) :

      <build>
        p<lugins
      >    pl<ugin
       >     gro<upIdcom>.google.cloud.tools/gr<oupId
        >    arti<factIdjib->maven-plugin/art<ifactId
         >   versi<on3.4.0>/vers<ion
          >  config<uration
           >   to
          <  >    imagegcr<.io/P>ROJECT_ID/hello-service/image
       <      > /to
           < /configura<tion
          /plug>in
        /<plugins>
      /bu<ild><>

  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>
      g<roupIdc>om.google.cloud.tools/<groupId
    >  ar<tifactIdji>b-maven-plugin/a<rtifactId
     > ver<sion3.4>.0/ve<rsion
      >conf<iguration
       > to
      <  >  imageg<cr.io>/PROJECT_ID/hello-service/image<
        />to
      /<con>figu<ration
    /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 Knative serving.

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

    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 Knative serving. Notez que l'image de conteneur est déployée sur le service et le cluster que vous avez configurés précédemment dans la section Configurer les paramètres par défaut de gcloud.

    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.

Si votre cluster est configuré avec un domaine par défaut routable, ignorez les étapes ci-dessus et copiez l'URL dans votre navigateur Web.

Si vous n'utilisez pas les certificats TLS automatiques ni le mappage de domaine, aucune URL navigable ne vous est fournie pour votre service.

Utilisez plutôt l'URL fournie et l'adresse IP de la passerelle d'entrée du service pour créer une commande curl pouvant envoyer des requêtes à votre service :

  1. Pour obtenir l'adresse IP externe de l'équilibreur de charge, exécutez la commande suivante :

    kubectl get svc istio-ingressgateway -n ASM-INGRESS-NAMESPACE
    

    Remplacez ASM-INGRESS-NAMESPACE par l'espace de noms où se trouve votre entrée Cloud Service Mesh. Spécifiez istio-system si vous avez installé Cloud Service Mesh à l'aide de sa configuration par défaut.

    La sortie ressemble à ceci :

    NAME                   TYPE           CLUSTER-IP     EXTERNAL-IP  PORT(S)
    istio-ingressgateway   LoadBalancer   XX.XX.XXX.XX   pending      80:32380/TCP,443:32390/TCP,32400:32400/TCP
    

    où la valeur EXTERNAL-IP est votre adresse IP externe de l'équilibreur de charge.

  2. Exécutez une commande curl en utilisant cette adresse GATEWAY_IP dans l'URL.

     curl -G -H "Host: SERVICE-DOMAIN" https://EXTERNAL-IP/

    Remplacez SERVICE-DOMAIN par le domaine par défaut de votre service. Pour ce faire, utilisez l'URL par défaut et supprimez le protocole http://.

  3. Consultez le message d'erreur HTTP 500 ou HTTP 503.

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 disponibles pour collecter plus de détails :

  1. Affichez les journaux pour obtenir plus de détails.

  2. Utilisez Cloud Logging pour examiner la séquence des opérations menant au problème, y compris les messages d'erreur.

Procéder au rollback vers la version correcte

Si vous avez déjà effectué une révision qui fonctionne, vous pouvez procéder au rollback sur votre service pour utiliser cette révision. Par exemple, vous ne pourrez pas effectuer de rollback sur le nouveau service hello-service que vous avez déployé dans ce tutoriel, car il ne contient qu'une seule révision.

Pour localiser une révision et effectuer un rollback sur votre service, procédez comme suit :

  1. Répertoriez toutes les révisions de votre service.

  2. Migrez tout le trafic vers la révision correcte.

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 Knative serving, ce qui confirme que le problème se manifeste lorsque le service est hébergé sur Knative serving. À 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 à Knative serving :

    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 un chemin d'accès au dépôt pointant vers la dernière version de l'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 Google Cloud Observability.

    Si le problème ne peut être reproduit localement, il peut être unique à l'environnement Knative serving. Consultez le guide de dépannage de Knative serving 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.

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 Knative serving en cours d'exécution pour inclure cette variable :

    1. Exécutez la commande de mise à jour des services avec le paramètre --update-env-vars pour ajouter une variable d'environnement :

      gcloud run services update hello-service \
        --update-env-vars NAME=Override
      
    2. Patientez quelques secondes pendant que Knative serving 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 Knative serving 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.

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

Effectuer un nettoyage

Vous pouvez supprimer les ressources créées pour ce tutoriel afin d'éviter que des frais ne vous soient facturés.

Supprimer les ressources du tutoriel

  1. Supprimez le service Knative serving 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 les services Knative serving à partir de la console Google Cloud.

    Accéder à Knative serving

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

     gcloud config unset run/platform
     gcloud config unset run/cluster
     gcloud config unset 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