Risoluzione dei problemi locali


Questo tutorial mostra come risolvere i problemi relativi a un servizio Knative Serving non funzionante utilizzando gli strumenti Stackdriver per l'individuazione e un flusso di lavoro di sviluppo locale per l'analisi.

Questa "case study" passo passo che accompagna la guida alla risoluzione dei problemi utilizza un progetto di esempio che genera errori di runtime durante il deployment, che risolvi per trovare e correggere il problema.

Obiettivi

  • Scrivi, crea ed esegui il deployment di un servizio su Knative serving
  • Utilizzare Cloud Logging per identificare un errore
  • Recupera l'immagine container da Container Registry per un'analisi della causa principale
  • Correggi il servizio "production", quindi miglioralo per mitigare i problemi futuri

Costi

In questo documento, utilizzi i seguenti componenti fatturabili di Google Cloud:

Per generare una stima dei costi in base all'utilizzo previsto, utilizza il calcolatore prezzi.

I nuovi Google Cloud utenti potrebbero avere diritto a una prova gratuita.

Prima di iniziare

Assemblaggio del codice

Crea un nuovo servizio di saluto Knative serving passo dopo passo. Ti ricordiamo che questo servizio crea intenzionalmente un errore di runtime per l'esercizio di risoluzione dei problemi.

  1. Crea un nuovo progetto:

    Node.js

    Crea un progetto Node.js definendo il pacchetto di servizi, le dipendenze iniziali e alcune operazioni comuni.

    1. Crea una directory hello-service:

      mkdir hello-service
      cd hello-service
      
    2. Genera un file package.json:

      npm init --yes
      npm install express@4
      
    3. Apri il nuovo file package.json nell'editor e configura uno script start per eseguire node index.js. Al termine, il file avrà un aspetto simile al seguente:

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

    Se continui a sviluppare questo servizio oltre il tutorial immediato, valuta la possibilità di compilare la descrizione, l'autore e la licenza. Per ulteriori dettagli, leggi la documentazione di package.json.

    Python

    1. Crea una nuova directory hello-service:

      mkdir hello-service
      cd hello-service
      
    2. Crea un file requirements.txt e copia le dipendenze al suo interno:

      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==23.0.0
      Werkzeug==3.0.3
      

    Vai

    1. Crea una directory hello-service:

      mkdir hello-service
      cd hello-service
      
    2. Crea un progetto Go inizializzando un nuovo modulo Go:

      go mod init <var>my-domain</var>.com/hello-service
      

    Puoi aggiornare il nome specifico come preferisci: devi aggiornare il nome se il codice viene pubblicato in un repository di codice accessibile dal web.

    Java

    1. Crea un progetto Maven:

      mvn archetype:generate \
        -DgroupId=com.example \
        -DartifactId=hello-service \
        -DarchetypeArtifactId=maven-archetype-quickstart \
        -DinteractiveMode=false
      
    2. Copia le dipendenze nell'elenco delle dipendenze pom.xml (tra gli elementi <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. Copia l'impostazione di build in pom.xml (sotto gli elementi <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. Crea un servizio HTTP per gestire le richieste in entrata:

    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. Crea un Dockerfile per definire l'immagine container utilizzata per il deployment del servizio:

    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 official Go image to create a binary.
    # This is based on Debian and sets the GOPATH to /go.
    # https://hub.docker.com/_/golang
    FROM golang:1.23-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

    Questo esempio utilizza Jib per creare immagini Docker utilizzando strumenti Java comuni. Jib ottimizza le build dei container senza la necessità di un Dockerfile o di Docker installato. Scopri di più sulla creazione di container Java con 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>
    

Invio del codice in corso…

Il codice di spedizione è costituito da tre passaggi: creazione di un'immagine container con Cloud Build, caricamento dell'immagine container in Container Registry e deployment dell'immagine container in Knative serving.

Per spedire il codice:

  1. Crea il container e pubblicalo in Container Registry:

    Node.js

    gcloud builds submit --tag gcr.io/PROJECT_ID/hello-service

    Dove PROJECT_ID è l'ID progetto Google Cloud . Puoi controllare l'ID progetto corrente con gcloud config get-value project.

    In caso di esito positivo, dovresti visualizzare un messaggio di operazione riuscita contenente l'ID, l'ora di creazione e il nome dell'immagine. L'immagine è archiviata in Container Registry e, se lo desideri, può essere riutilizzata.

    Python

    gcloud builds submit --tag gcr.io/PROJECT_ID/hello-service

    Dove PROJECT_ID è l'ID progetto Google Cloud . Puoi controllare l'ID progetto corrente con gcloud config get-value project.

    In caso di esito positivo, dovresti visualizzare un messaggio di operazione riuscita contenente l'ID, l'ora di creazione e il nome dell'immagine. L'immagine è archiviata in Container Registry e, se lo desideri, può essere riutilizzata.

    Vai

    gcloud builds submit --tag gcr.io/PROJECT_ID/hello-service

    Dove PROJECT_ID è l'ID progetto Google Cloud . Puoi controllare l'ID progetto corrente con gcloud config get-value project.

    In caso di esito positivo, dovresti visualizzare un messaggio di operazione riuscita contenente l'ID, l'ora di creazione e il nome dell'immagine. L'immagine è archiviata in Container Registry e, se lo desideri, può essere riutilizzata.

    Java

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

    Dove PROJECT_ID è l'ID progetto Google Cloud . Puoi controllare l'ID progetto corrente con gcloud config get-value project.

    In caso di esito positivo, dovresti visualizzare un messaggio che indica che la build è stata creata correttamente. L'immagine è archiviata in Container Registry e, se lo desideri, può essere riutilizzata.

  2. Esegui questo comando per eseguire il deployment dell'app:

    gcloud run deploy hello-service --image gcr.io/PROJECT_ID/hello-service

    Sostituisci PROJECT_ID con l'ID del tuo progetto Google Cloud . hello-service è sia il nome dell'immagine container sia il nome del servizio Knative serving. Tieni presente che l'immagine container viene sottoposta a deployment nel servizio e nel cluster che hai configurato in precedenza in Configurazione di gcloud.

    Attendi il completamento del deployment, che può richiedere circa 30 secondi. Se l'operazione riesce, la riga di comando visualizza l'URL del servizio.

Prova

Prova il servizio per verificare che il deployment sia stato eseguito correttamente. Le richieste devono non riuscire con un errore HTTP 500 o 503 (membri della classe 5xx Server errors). Il tutorial illustra la procedura per risolvere il problema relativo a questa risposta di errore.

Se il cluster è configurato con un dominio predefinito instradabile, salta i passaggi precedenti e copia l'URL nel browser web.

Se non utilizzi i certificati TLS automatici e la mappatura dei domini, non ti viene fornito un URL navigabile per il tuo servizio.

Utilizza invece l'URL fornito e l'indirizzo IP del gateway di ingresso del servizio per creare un comando curl che possa effettuare richieste al tuo servizio:

  1. Per ottenere l'IP esterno del bilanciatore del carico, esegui questo comando:

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

    Sostituisci ASM-INGRESS-NAMESPACE con lo spazio dei nomi in cui si trova l'ingresso di Cloud Service Mesh. Specifica istio-system se hai installato Cloud Service Mesh utilizzando la configurazione predefinita.

    L'output risultante è simile al seguente:

    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

    dove il valore EXTERNAL-IP è l'indirizzo IP esterno del bilanciatore del carico.

  2. Esegui un comando curl utilizzando questo indirizzo GATEWAY_IP nell'URL.

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

    Sostituisci SERVICE-DOMAIN con il dominio assegnato predefinito del tuo servizio. Puoi ottenerlo prendendo l'URL predefinito e rimuovendo il protocollo http://.

  3. Visualizzare il messaggio di errore HTTP 500 o HTTP 503.

Analisi del problema

Visualizza l'errore HTTP 5xx riscontrato in precedenza nella sezione Prova come errore di runtime di produzione. Questo tutorial illustra una procedura formale per gestirlo. Sebbene le procedure di risoluzione degli errori di produzione varino notevolmente, questo tutorial presenta una sequenza particolare di passaggi per mostrare l'applicazione di strumenti e tecniche utili.

Per analizzare il problema, dovrai seguire queste fasi:

  • Raccogli ulteriori dettagli sull'errore segnalato per supportare ulteriori indagini e definire una strategia di mitigazione.
  • Ridurre l'impatto sugli utenti decidendo di procedere con una correzione o di eseguire il rollback a una versione integra nota.
  • Riproduci l'errore per verificare che siano stati raccolti i dettagli corretti e che l'errore non sia un problema temporaneo
  • Esegui un'analisi delle cause principali del bug per trovare il codice, la configurazione o il processo che ha creato questo errore

All'inizio dell'indagine hai un URL, un timestamp e il messaggio "Internal Server Error".

Raccolta di ulteriori dettagli

Raccogliere ulteriori informazioni sul problema per capire cosa è successo e determinare i passaggi successivi.

Utilizza gli strumenti disponibili per raccogliere maggiori dettagli:

  1. Visualizza i log per maggiori dettagli.

  2. Utilizza Cloud Logging per esaminare la sequenza di operazioni che ha portato al problema, inclusi i messaggi di errore.

Esegui il rollback a una versione integra

Se hai una revisione che sai funzionare, puoi eseguire il rollback del servizio per utilizzare quella revisione. Ad esempio, non potrai eseguire il rollback del nuovo servizio hello-service che hai implementato in questo tutorial perché contiene una sola revisione.

Per individuare una revisione e ripristinare il tuo servizio:

  1. Elenca tutte le revisioni del servizio.

  2. Esegui la migrazione di tutto il traffico alla revisione integra.

Riproduzione dell'errore

Utilizzando i dettagli ottenuti in precedenza, verifica che il problema si verifichi in modo coerente nelle condizioni di test.

Invia la stessa richiesta HTTP riprovando e verifica se vengono segnalati lo stesso errore e gli stessi dettagli. Potrebbe essere necessario del tempo prima che vengano visualizzati i dettagli dell'errore.

Poiché il servizio di esempio in questo tutorial è di sola lettura e non attiva effetti collaterali complicati, la riproduzione degli errori in produzione è sicura. Tuttavia, per molti servizi reali, non sarà così: potrebbe essere necessario riprodurre gli errori in un ambiente di test o limitare questo passaggio all'analisi locale.

La riproduzione dell'errore stabilisce il contesto per ulteriori interventi. Ad esempio, se gli sviluppatori non riescono a riprodurre l'errore, ulteriori indagini potrebbero richiedere strumenti aggiuntivi per il servizio.

Esecuzione di un'analisi delle cause principali

L'analisi della causa principale è un passaggio importante per una risoluzione dei problemi efficace per assicurarti di risolvere il problema invece di un sintomo.

In precedenza in questo tutorial, hai riprodotto il problema su Knative serving, il che conferma che il problema è attivo quando il servizio è ospitato su Knative serving. Ora riproduci il problema localmente per determinare se è isolato nel codice o se si verifica solo nell'hosting di produzione.

  1. Se non hai utilizzato Docker CLI localmente con Container Registry, autenticalo con gcloud:

    gcloud auth configure-docker

    Per approcci alternativi, vedi Metodi di autenticazione di Container Registry.

  2. Se il nome dell'immagine container utilizzata più di recente non è disponibile, la descrizione del servizio contiene le informazioni sull'immagine container di cui è stato eseguito il deployment più di recente:

    gcloud run services describe hello-service

    Trova il nome dell'immagine container all'interno dell'oggetto spec. Un comando più mirato può recuperarlo direttamente:

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

    Questo comando mostra un nome dell'immagine container, ad esempio gcr.io/PROJECT_ID/hello-service.

  3. Estrai l'immagine container da Container Registry nel tuo ambiente. Questo passaggio potrebbe richiedere diversi minuti per il download dell'immagine container:

    docker pull gcr.io/PROJECT_ID/hello-service

    Gli aggiornamenti successivi all'immagine container che riutilizzano questo nome possono essere recuperati con lo stesso comando. Se salti questo passaggio, il comando docker run riportato di seguito esegue il pull di un'immagine container se non è presente sulla macchina locale.

  4. Esegui localmente per verificare che il problema non sia specifico di Knative serving:

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

    Analizzando gli elementi del comando precedente,

    • La variabile di ambiente PORT viene utilizzata dal servizio per determinare la porta da ascoltare all'interno del container.
    • Il comando run avvia il container, utilizzando per impostazione predefinita il comando entrypoint definito nel Dockerfile o in un'immagine container padre.
    • Il flag --rm elimina l'istanza del container all'uscita.
    • Il flag -e assegna un valore a una variabile di ambiente. -e PORT=$PORT sta propagando la variabile PORT dal sistema locale al contenitore con lo stesso nome della variabile.
    • Il flag -p pubblica il contenitore come servizio disponibile su localhost sulla porta 9000. Le richieste a localhost:9000 verranno instradate al container sulla porta 8080. Ciò significa che l'output del servizio relativo al numero di porta in uso non corrisponderà alla modalità di accesso al servizio.
    • L'ultimo argomento gcr.io/PROJECT_ID/hello-service è un percorso del repository che punta all'ultima versione dell'immagine del container. Se non è disponibile localmente, Docker tenta di recuperare l'immagine da un registro remoto.

    Nel browser, apri http://localhost:9000. Controlla l'output del terminale per messaggi di errore che corrispondono a quelli di Google Cloud Observability.

    Se il problema non è riproducibile localmente, potrebbe essere specifico dell'ambiente di serving Knative. Consulta la guida alla risoluzione dei problemi di Knative serving per aree specifiche da esaminare.

    In questo caso, l'errore viene riprodotto localmente.

Ora che l'errore è stato confermato due volte come persistente e causato dal codice del servizio anziché dalla piattaforma di hosting, è il momento di esaminare il codice più da vicino.

Ai fini di questo tutorial, è sicuro presupporre che il codice all'interno del container e il codice nel sistema locale siano identici.

Node.js

Trova l'origine del messaggio di errore nel file index.js intorno al numero di riga indicato nellaanalisi dello stackk mostrata nei log:
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

Trova l'origine del messaggio di errore nel file main.py intorno al numero di riga indicato nellaanalisi dello stackk mostrata nei log:
NAME = os.getenv("NAME")

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

Vai

Trova l'origine del messaggio di errore nel file main.go intorno al numero di riga indicato nellaanalisi dello stackk mostrata nei log:

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

Trova l'origine del messaggio di errore nel file App.java intorno al numero di riga indicato nello analisi dello stack mostrato nei log:

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

Esaminando questo codice, vengono eseguite le seguenti azioni quando la variabile di ambiente NAME non è impostata:

  • Viene registrato un errore in Google Cloud Observability
  • Viene inviata una risposta di errore HTTP

Il problema è causato da una variabile mancante, ma la causa principale è più specifica: la modifica del codice che aggiunge la dipendenza rigida da una variabile di ambiente non include modifiche correlate agli script di deployment e alla documentazione dei requisiti di runtime.

Risoluzione della causa principale

Ora che abbiamo raccolto il codice e identificato la potenziale causa principale, possiamo adottare le misure necessarie per risolvere il problema.

  • Controlla se il servizio funziona localmente con l'ambiente NAME disponibile:

    1. Esegui il container localmente con la variabile di ambiente aggiunta:

      PORT=8080 && docker run --rm -e PORT=$PORT -p 9000:$PORT \
       -e NAME="Local World!" \
       gcr.io/PROJECT_ID/hello-service
    2. Nel browser, vai alla pagina http://localhost:9000

    3. Visualizzare "Hello Local World!" sulla pagina

  • Modifica l'ambiente del servizio Knative serving in esecuzione in modo da includere questa variabile:

    1. Esegui il comando di aggiornamento dei servizi con il parametro --update-env-vars per aggiungere una variabile di ambiente:

      gcloud run services update hello-service \
        --update-env-vars NAME=Override
      
    2. Attendi qualche secondo mentre Knative serving crea una nuova revisione basata sulla revisione precedente con la nuova variabile di ambiente aggiunta.

  • Conferma che il servizio è stato riparato:

    1. Nel browser, vai all'URL del servizio Knative serving.
    2. Visualizza "Hello Override!" nella pagina.
    3. Verifica che in Cloud Logging non vengano visualizzati messaggi o errori imprevisti.

Miglioramento della velocità di risoluzione dei problemi futuri

In questo problema di produzione di esempio, l'errore era correlato alla configurazione operativa. Sono state apportate modifiche al codice che ridurranno al minimo l'impatto di questo problema in futuro.

  • Migliora il log degli errori per includere dettagli più specifici.
  • Anziché restituire un errore, il servizio deve eseguire il failover su un valore predefinito sicuro. Se l'utilizzo di un valore predefinito rappresenta una modifica alla funzionalità normale, utilizza un messaggio di avviso a scopo di monitoraggio.

Vediamo come rimuovere la variabile di ambiente NAME come dipendenza rigida.

  1. Rimuovi il codice di gestione di NAME esistente:

    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. Aggiungi un nuovo codice che imposta un valore di riserva:

    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. Esegui il test in locale ricompilando ed eseguendo il container nei casi di configurazione interessati:

    Node.js

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

    Python

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

    Vai

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

    Java

    mvn compile jib:build

    Verifica che la variabile di ambiente NAME funzioni ancora:

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

    Verifica che il servizio funzioni senza la variabile NAME:

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

    Se il servizio non restituisce un risultato, verifica che la rimozione del codice nel primo passaggio non abbia rimosso righe aggiuntive, ad esempio quelle utilizzate per scrivere la risposta.

  4. Esegui il deployment tornando alla sezione Esegui il deployment del codice.

    Ogni deployment in un servizio crea una nuova revisione e inizia automaticamente a gestire il traffico quando è pronto.

    Per cancellare le variabili di ambiente impostate in precedenza:

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

Aggiungi la nuova funzionalità per il valore predefinito alla copertura dei test automatizzati per il servizio.

Trovare altri problemi nei log

Potresti notare altri problemi nel visualizzatore log per questo servizio. Ad esempio, una chiamata di sistema non supportata verrà visualizzata nei log come "Limitazione sandbox del container".

Ad esempio, i servizi Node.js a volte generano questo messaggio di log:

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.

In questo caso, la mancanza di supporto non influisce sul servizio di esempio hello-service.

Esegui la pulizia

Puoi eliminare le risorse create per questo tutorial per evitare costi.

Eliminazione delle risorse del tutorial

  1. Elimina il servizio Knative serving di cui hai eseguito il deployment in questo tutorial:

    gcloud run services delete SERVICE-NAME

    Dove SERVICE-NAME è il nome del servizio che hai scelto.

    Puoi anche eliminare i servizi Knative serving dalla console Google Cloud :

    Vai a Knative serving

  2. Rimuovi le configurazioni predefinite di gcloud che hai aggiunto durante la configurazione del tutorial:

     gcloud config unset run/platform
     gcloud config unset run/cluster
     gcloud config unset run/cluster_location
    
  3. Rimuovi la configurazione del progetto:

     gcloud config unset project
    
  4. Elimina le altre risorse Google Cloud create in questo tutorial:

Passaggi successivi