Risoluzione dei problemi locali


Questo tutorial mostra come uno sviluppatore di servizi può risolvere i problemi di un servizio Cloud Run for Anthos non funzionante utilizzando gli strumenti Stackdriver per il rilevamento e un flusso di lavoro di sviluppo locale per l'indagine.

Questa guida passo passo per il "case study" della guida alla risoluzione dei problemi utilizza un progetto di esempio che genera errori di runtime durante il deployment e consente di risolvere i problemi e trovare una soluzione.

Tieni presente che non puoi utilizzare questo tutorial con Cloud Run for Anthos su VMware a causa dei limiti di supporto della suite operativa di Google Cloud.

Obiettivi

  • Scrivi, crea ed esegui il deployment di un servizio su Cloud Run for Anthos
  • Utilizzare Cloud Logging per identificare un errore
  • Recupera l'immagine container da Container Registry per un'analisi delle cause principali
  • 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 basata sull'utilizzo previsto, utilizza il Calcolatore prezzi. I nuovi utenti di Google Cloud potrebbero essere idonei per una prova gratuita.

Prima di iniziare

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

    Go to project selector

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

  3. Abilita l'API Cloud Run for Anthos
  4. Installa e inizializza Google Cloud CLI.
  5. Installa il componente kubectl:
    gcloud components install kubectl
  6. Aggiorna i componenti:
    gcloud components update
  7. Se utilizzi Cloud Run for Anthos, crea un nuovo cluster seguendo le istruzioni della configurazione di Cloud Run for Anthos.
  8. Se utilizzi Cloud Run for Anthos, installa curl per provare il servizio
  9. Segui le istruzioni per installare Docker in locale

Configurazione dei valori predefiniti gcloud

Per configurare gcloud con i valori predefiniti per il servizio Cloud Run for Anthos:

  1. Imposta il progetto predefinito:

    gcloud config set project PROJECT_ID

    Sostituisci PROJECT_ID con il nome del progetto che utilizzi per questo tutorial.

  2. Configura gcloud per il tuo cluster:

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

    Sostituisci:

    • CLUSTER-NAME con il nome utilizzato per il cluster,
    • REGION con la località del cluster supportata di tua scelta.

Assemblaggio del codice

Crea un nuovo servizio di grembiule Cloud Run for Anthos passo dopo passo. Ti ricordiamo che questo servizio crea un errore di runtime apposito per l'esercizio della risoluzione dei problemi.

  1. Crea un nuovo progetto:

    Node.js

    Per creare un progetto Node.js, definisci il pacchetto di servizi, le dipendenze iniziali e alcune operazioni comuni.

    1. Crea una nuova directory hello-service:

      mkdir hello-service
      cd hello-service
      
    2. Crea un nuovo progetto Node.js generando un file package.json:

      npm init --yes
      npm install --save 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 sarà simile a questo:

      {
        "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 evolvere questo servizio oltre il tutorial immediato, valuta se inserire la descrizione, l'autore e valutare la licenza. Per maggiori dettagli, leggi la documentazione di package.json.

    Python

    1. Crea una nuova directory hello-service:

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

      Flask==2.1.0
      pytest==7.0.1; python_version > "3.0"
      # pin pytest to 4.6.11 for Python2.
      pytest==7.0.1; python_version < "3.0"
      gunicorn==20.1.0
      

    Go

    1. Crea una nuova directory hello-service:

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

      go mod init example.com/hello-service
      

    Puoi aggiornare il nome specifico come preferisci: devi aggiornare il nome se il codice è pubblicato in un repository di codice raggiungibile sul Web.

    Java

    1. Crea un nuovo 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.7</version>
      </dependency>
      <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-simple</artifactId>
        <version>2.0.7</version>
      </dependency>
      
    3. Copia l'impostazione di compilazione in pom.xml (sotto gli elementi <dependencies>):

      <build>
        <plugins>
          <plugin>
            <groupId>com.google.cloud.tools</groupId>
            <artifactId>jib-maven-plugin</artifactId>
            <version>3.3.2</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:18-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 production dependencies.
    # If you add a package-lock.json, speed your build by switching to 'npm ci'.
    # RUN npm ci --only=production
    RUN npm install --only=production
    
    # Copy local code to the container image.
    COPY . ./
    
    # Run the web service on container startup.
    CMD [ "npm", "start" ]
    

    Python

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

    Java

    Questo esempio utilizza Jib per creare immagini Docker utilizzando strumenti Java comuni. Jib ottimizza le build dei container senza bisogno di un Dockerfile o senza installare Docker. Scopri di più sulla creazione di container Java con Jib.

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

Spedizione del codice

Il codice di spedizione è composto da tre passaggi: la creazione di un'immagine container con Cloud Build, il caricamento dell'immagine container in Container Registry e il deployment dell'immagine container su Cloud Run for Anthos.

Per spedire il codice:

  1. Crea il container e pubblicalo su Container Registry:

    Node.js

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

    Dove PROJECT_ID è il tuo ID progetto GCP. Puoi verificare il tuo ID progetto corrente con gcloud config get-value project.

    In caso di esito positivo, dovresti vedere 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 necessario, può essere riutilizzata.

    Python

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

    Dove PROJECT_ID è il tuo ID progetto GCP. Puoi verificare il tuo ID progetto corrente con gcloud config get-value project.

    In caso di esito positivo, dovresti vedere 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 necessario, può essere riutilizzata.

    Go

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

    Dove PROJECT_ID è il tuo ID progetto GCP. Puoi verificare il tuo ID progetto corrente con gcloud config get-value project.

    In caso di esito positivo, dovresti vedere 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 necessario, può essere riutilizzata.

    Java

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

    Dove PROJECT_ID è il tuo ID progetto GCP. Puoi verificare il tuo ID progetto corrente con gcloud config get-value project.

    In caso di esito positivo, dovresti visualizzare un messaggio BUILD SUCCESS (Stabilisci edificio). 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 progetto GCP. hello-service è il nome e il nome dell'immagine container del servizio Cloud Run for Anthos. Nota che viene eseguito il deployment dell'immagine container sul servizio e sul cluster configurati in precedenza in Configurazione di gcloud

    Attendi il completamento del deployment: questa operazione può richiedere circa mezzo minuto. Se l'operazione riesce, la riga di comando visualizza l'URL del servizio.

Prova in corso

Prova il servizio per verificare che sia stato eseguito correttamente il deployment. Le richieste non vanno a buon fine con un errore HTTP 500 o 503 (membri della classe Errori server 5xx). Il tutorial illustra la risoluzione di questo errore.

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

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

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

  1. Per ottenere l'IP esterno per il gateway in entrata Istio:
    kubectl get svc istio-ingress -n gke-system
    in cui l'output risultante ha il seguente aspetto:
    NAME            TYPE           CLUSTER-IP     EXTERNAL-IP  PORT(S)
    istio-ingress   LoadBalancer   XX.XX.XXX.XX   pending      80:32380/TCP,443:32390/TCP,32400:32400/TCP
    Il EXTERNAL-IP per il bilanciatore del carico è l'indirizzo IP che devi utilizzare.
  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. Per ottenere questa informazione, rimuovi l'URL predefinito e rimuovi il protocollo http://.

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

Esaminare il problema

Visualizza l'errore HTTP 5xx che hai visto sopra in Provala e che si è verificato come errore di runtime di produzione. Questo tutorial illustra un processo formale di gestione. Sebbene i processi di risoluzione degli errori di produzione siano molto diversi, questo tutorial presenta una particolare sequenza di passaggi per mostrare l'applicazione di strumenti e tecniche utili.

Per esaminare il problema, eseguirai le seguenti fasi:

  • Raccogliere maggiori dettagli sull'errore segnalato per supportare ulteriori indagini e impostare una strategia di mitigazione.
  • Alleviare l'impatto degli utenti decidendo di effettuare il push in una correzione o il rollback a una versione nota dello stato integro.
  • Riproduci l'errore per confermare che sono stati raccolti i dettagli corretti e che l'errore non sia un problema singolo.
  • Eseguire un'analisi delle cause principali sul bug per trovare il codice, la configurazione o il processo che ha generato l'errore

All'inizio dell'indagine sono indicati un URL, il timestamp e il messaggio "Errore interno del server".

Raccolta di ulteriori dettagli

Raccogli altre 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 portano al problema, inclusi i messaggi di errore.

Esegui il rollback a una versione integra

Se hai una revisione che sai che stava funzionando, puoi eseguire il rollback del tuo servizio per utilizzare la revisione. Ad esempio, non potrai eseguire un rollback sul nuovo servizio hello-service di cui hai eseguito il deployment in questo tutorial perché contiene una sola revisione.

Per trovare una revisione ed eseguire il rollback del 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 in condizioni di test.

Invia la stessa richiesta HTTP provandola di nuovo e controlla se vengono segnalati lo stesso errore e gli stessi dettagli. La visualizzazione dei dettagli dell'errore potrebbe richiedere del tempo.

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

La riproduzione dell'errore stabilisce il contesto per un ulteriore lavoro. Ad esempio, se gli sviluppatori non sono in grado di riprodurre l'errore, ulteriori indagini possono richiedere una strumentazione aggiuntiva del servizio.

Esecuzione di un'analisi delle cause principali

L'analisi delle cause principali è un passaggio importante nella risoluzione dei problemi efficace per assicurarti di risolvere il problema anziché mostrare un sintomo.

In precedenza, in questo tutorial hai riprodotto il problema su Cloud Run for Anthos, che conferma che il problema è attivo quando il servizio è ospitato su Cloud Run for Anthos. Ora riproduci il problema localmente per determinare se il problema è isolato al codice o se emerge solo nell'hosting di produzione.

  1. Se non hai utilizzato l'interfaccia a riga di comando di Docker localmente con Container Registry, esegui l'autenticazione con gcloud:

    gcloud auth configure-docker

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

  2. Se non è disponibile il nome dell'immagine container utilizzato più di recente, 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 rivela un nome di immagine container come gcr.io/PROJECT_ID/hello-service.

  3. Esegui il pull dell'immagine container da Container Registry nel tuo ambiente: questo passaggio potrebbe richiedere diversi minuti durante il download dell'immagine container:

    docker pull gcr.io/PROJECT_ID/hello-service

    Successivamente, è possibile recuperare gli aggiornamenti successivi all'immagine container che riutilizzano questo nome con lo stesso comando. Se salti questo passaggio, il comando docker run di seguito esegue il pull di un'immagine container se non è presente sulla macchina locale.

  4. Esegui l'esecuzione in locale per verificare che il problema non sia univoco per Cloud Run for Anthos:

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

    L'analisi degli elementi del comando precedente

    • La variabile di ambiente PORT viene utilizzata dal servizio per determinare la porta su cui ascoltare all'interno del container.
    • Il comando run avvia il container e usa per impostazione predefinita il comando entrypoint definito nel Dockerfile o in un'immagine container padre.
    • Il flag --rm elimina l'istanza di 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 nel container con lo stesso nome di variabile.
    • Il flag -p pubblica il container come servizio disponibile su localhost alla 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à alle modalità di accesso al servizio.
    • L'argomento finale gcr.io/PROJECT_ID/hello-service è un percorso del repository che rimanda all'ultima versione dell'immagine container. Se non è disponibile localmente, il docker tenta di recuperare l'immagine da un registro remoto.

    Nel browser, apri http://localhost:9000. Verifica l'output del terminale per individuare i messaggi di errore che corrispondono a quelli della suite operativa di Google Cloud.

    Se il problema non è riproducibile localmente, potrebbe essere univoco nell'ambiente Cloud Run for Anthos. Consulta la guida alla risoluzione dei problemi di Cloud Run for Anthos per aree specifiche da esaminare.

    In questo caso, l'errore viene riprodotto localmente.

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

Ai fini di questo tutorial, puoi presumere che il codice all'interno del container sia identico nel codice del sistema locale.

Node.js

Individua l'origine del messaggio di errore nel file index.js attorno al numero di riga indicato nell'analisi dello stack 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

Individua l'origine del messaggio di errore nel file main.py attorno al numero di riga indicato nell'analisi dello stack mostrata nei log:
NAME = os.getenv("NAME")

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

Go

Individua l'origine del messaggio di errore nel file main.go attorno al numero di riga indicato nell'analisi dello stack 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

Individua l'origine del messaggio di errore nel file App.java intorno al numero di riga indicato nell'analisi dello stack mostrata 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";
}

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

  • Viene registrato un errore nella suite operativa di Google Cloud
  • 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 ha incluso le modifiche correlate agli script di deployment e alla documentazione sui requisiti di runtime.

Correzione della causa principale

Dopo aver raccolto il codice e identificato la potenziale causa principale, possiamo adottare misure per risolvere il problema.

  • Verifica 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. Accedi al browser all'indirizzo http://localhost:9000

    3. Sulla pagina viene visualizzato il messaggio "Hello Local World!".

  • Modifica l'ambiente di servizio Cloud Run for Anthos 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 Cloud Run for Anthos crea una nuova revisione in base alla revisione precedente con l'aggiunta della nuova variabile di ambiente.

  • Verifica che il servizio sia stato corretto:

    1. Vai nel browser dell'URL del servizio Cloud Run for Anthos.
    2. Sulla pagina viene visualizzato il messaggio "Override di Hello".
    3. Verifica che in Cloud Logging non vengano visualizzati messaggi o errori imprevisti.

Migliorare la velocità di risoluzione dei problemi in futuro

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

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

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

  1. Rimuovi il codice di gestione esistente di NAME:

    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. Testa a livello locale la creazione e l'esecuzione del container tramite i 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 .

    Go

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

    Java

    mvn compile jib:build

    Verifica che la variabile di ambiente NAME continui a funzionare:

    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, come quelle utilizzate per scrivere la risposta.

  4. Per eseguire il deployment, rivedi la 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 automatici per il servizio.

Individuare altri problemi nei log

Potresti riscontrare altri problemi nel visualizzatore log per questo servizio. Ad esempio, una chiamata di sistema non supportata verrà visualizzata nei log con la dicitura "Container Sandbox Limiting".

Ad esempio, a volte i servizi Node.js 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 assistenza non influisce sul servizio di esempio hello-service.

Esegui la pulizia

Se hai creato un nuovo progetto per questo tutorial, elimina il progetto. Se hai utilizzato un progetto esistente e vuoi mantenerlo senza le modifiche aggiunte in questo tutorial, elimina le risorse create per il tutorial.

Elimina il progetto

Il modo più semplice per eliminare la fatturazione è eliminare il progetto che hai creato per il tutorial.

Per eliminare il progetto:

  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.

Eliminazione delle risorse del tutorial

  1. Elimina il servizio Cloud Run for Anthos 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 Cloud Run for Anthos dalla console Google Cloud:

    Vai a Cloud Run for Anthos

  2. Rimuovi le configurazioni gcloud predefinite 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 altre risorse Google Cloud create in questo tutorial:

Passaggi successivi

  • Scopri di più su come utilizzare Cloud Logging per ottenere insight sul comportamento di produzione.
  • Per maggiori informazioni sulla risoluzione dei problemi di Cloud Run for Anthos, consulta [/anthos/run/archive/docs/troubleshooter#sandbox".
  • Esplora architetture di riferimento, diagrammi e best practice su Google Cloud. Dai un'occhiata al nostro Centro di architettura cloud.