Tutorial: risoluzione dei problemi locali di un servizio Cloud Run


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

Questa procedura complementare dettagliata del "case study" della guida alla risoluzione dei problemi utilizza un progetto di esempio che, al momento del deployment, genera errori di runtime, che potrai risolvere per individuare e risolvere il problema.

Obiettivi

  • Scrivi, crea ed esegui il deployment di un servizio in Cloud Run
  • Utilizzare Error Reporting e Cloud Logging per identificare un errore
  • Recupera l'immagine container da Container Registry per un'analisi delle cause principali
  • Correggi il servizio di "produzione", quindi migliora il servizio per mitigare i problemi futuri

Costi

In questo documento vengono utilizzati i seguenti componenti fatturabili di Google Cloud:

Per generare una stima dei costi in base all'utilizzo previsto, utilizza il Calcolatore prezzi. I nuovi utenti di Google Cloud possono essere idonei a una prova senza costi aggiuntivi.

Prima di iniziare

  1. Accedi al tuo account Google Cloud. Se non conosci Google Cloud, crea un account per valutare le prestazioni dei nostri prodotti in scenari reali. I nuovi clienti ricevono anche 300 $di crediti gratuiti per l'esecuzione, il test e il deployment dei carichi di lavoro.
  2. Nella pagina del selettore di progetti della console Google Cloud, seleziona o crea un progetto Google Cloud.

    Vai al selettore progetti

  3. Assicurati che la fatturazione sia attivata per il tuo progetto Google Cloud.

  4. Nella pagina del selettore di progetti della console Google Cloud, seleziona o crea un progetto Google Cloud.

    Vai al selettore progetti

  5. Assicurati che la fatturazione sia attivata per il tuo progetto Google Cloud.

  6. Abilita l'API Cloud Run Admin
  7. Installa e inizializza gcloud CLI.
  8. Aggiorna componenti:
    gcloud components update
  9. Segui le istruzioni per installare Docker localmente

Ruoli obbligatori

Per ottenere le autorizzazioni necessarie per completare il tutorial, chiedi all'amministratore di concederti i seguenti ruoli IAM sul tuo progetto:

Per saperne di più sulla concessione dei ruoli, consulta Gestire l'accesso.

Potresti anche essere in grado di ottenere le autorizzazioni richieste tramite i ruoli personalizzati o altri ruoli predefiniti.

Configurazione delle impostazioni predefinite di gcloud

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

  1. Imposta il progetto predefinito:

    gcloud config set project PROJECT_ID

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

  2. Configura gcloud per la regione scelta:

    gcloud config set run/region REGION

    Sostituisci REGION con un'area geografica Cloud Run supportata a tua scelta.

Località di Cloud Run

Cloud Run è regionale, il che significa che l'infrastruttura che esegue i tuoi servizi Cloud Run si trova in una regione specifica ed è gestita da Google per essere disponibile in modo ridondante in tutte le zone all'interno di quella regione.

Soddisfare i requisiti di latenza, disponibilità o durabilità sono fattori principali per selezionare la regione in cui vengono eseguiti i servizi Cloud Run. In genere, puoi selezionare la regione più vicina ai tuoi utenti, ma ti consigliamo di considerare la località degli altri prodotti Google Cloud utilizzati dal tuo servizio Cloud Run. L'utilizzo combinato di prodotti Google Cloud in più località può influire sulla latenza e sui costi del tuo servizio.

Cloud Run è disponibile nelle seguenti regioni:

Soggetto ai prezzi di Livello 1

Soggetto ai prezzi di Livello 2

  • africa-south1 (Johannesburg)
  • asia-east2 (Hong Kong)
  • asia-northeast3 (Seul, Corea del Sud)
  • asia-southeast1 (Singapore)
  • asia-southeast2 (Giacarta)
  • asia-south1 (Mumbai, India)
  • asia-south2 (Delhi, India)
  • australia-southeast1 (Sydney)
  • australia-southeast2 (Melbourne)
  • europe-central2 (Varsavia, Polonia)
  • europe-west10 (Berlino)
  • europe-west12 (Torino)
  • europe-west2 (Londra, Regno Unito) icona foglia A basse emissioni di CO2
  • europe-west3 (Francoforte, Germania) icona foglia A basse emissioni di CO2
  • europe-west6 (Zurigo, Svizzera) icona foglia A basse emissioni di CO2
  • me-central1 (Doha)
  • me-central2 (Dammam)
  • northamerica-northeast1 (Montreal) icona foglia A basse emissioni di CO2
  • northamerica-northeast2 (Toronto) icona foglia A basse emissioni di CO2
  • southamerica-east1 (San Paolo, Brasile) icona foglia A basse emissioni di CO2
  • southamerica-west1 (Santiago, Cile) icona foglia A basse emissioni di CO2
  • us-west2 (Los Angeles)
  • us-west3 (Salt Lake City)
  • us-west4 (Las Vegas)

Se hai già creato un servizio Cloud Run, puoi visualizzare la regione nella dashboard di Cloud Run nella console Google Cloud.

Assemblaggio del codice

Crea un nuovo servizio di chat di Cloud Run passo passo. Ti ricordiamo che questo servizio crea appositamente 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 servizio, 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-broken",
        "description": "Broken Cloud Run service for troubleshooting practice",
        "version": "1.0.0",
        "private": true,
        "main": "index.js",
        "scripts": {
          "start": "node index.js",
          "test": "echo \"Error: no test specified\" && exit 0",
          "system-test": "NAME=Cloud c8 mocha -p -j 2 test/system.test.js --timeout=360000 --exit"
        },
        "engines": {
          "node": ">=16.0.0"
        },
        "author": "Google LLC",
        "license": "Apache-2.0",
        "dependencies": {
          "express": "^4.17.1"
        },
        "devDependencies": {
          "c8": "^8.0.0",
          "google-auth-library": "^9.0.0",
          "got": "^11.0.0",
          "mocha": "^10.0.0"
        }
      }
      

    Se continui a sviluppare questo servizio oltre il tutorial immediato, ti consigliamo di compilare la descrizione, l'autore e valutare la licenza. Per ulteriori dettagli, leggi la documentazione dipackage.json.

    Python

    1. Crea una nuova directory hello-service:

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

      Flask==3.0.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==22.0.0
      Werkzeug==3.0.1
      

    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 aggiornarlo se il codice viene pubblicato in un repository di codice raggiungibile dal web.

    Java

    1. Crea un nuovo progetto Maven:

      mvn archetype:generate \
        -DgroupId=com.example.cloudrun \
        -DartifactId=hello-service \
        -DarchetypeArtifactId=maven-archetype-quickstart \
        -DinteractiveMode=false
      
    2. Copia le dipendenze nel tuo elenco di 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 oggetto 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 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

    Questo esempio utilizza Jib per creare immagini Docker con gli strumenti Java comuni. Jib ottimizza le build di container senza bisogno di un Dockerfile o di dover 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.4.0</version>
      <configuration>
        <to>
          <image>gcr.io/PROJECT_ID/hello-service</image>
        </to>
      </configuration>
    </plugin>
    

Spedisci il codice

Il codice di spedizione prevede tre passaggi: creazione di un'immagine container con Cloud Build, caricamento dell'immagine container in Container Registry e deployment dell'immagine container in Cloud Run.

Per spedire il tuo codice:

  1. Crea il tuo 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 Google Cloud. Puoi controllare il tuo ID progetto attuale con gcloud config get-value project.

    Se l'operazione va a buon fine, dovresti visualizzare un messaggio SUCCESSIVO 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 è il tuo ID progetto Google Cloud. Puoi controllare il tuo ID progetto attuale con gcloud config get-value project.

    Se l'operazione va a buon fine, dovresti visualizzare un messaggio SUCCESSIVO 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.

    Go

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

    Dove PROJECT_ID è il tuo ID progetto Google Cloud. Puoi controllare il tuo ID progetto attuale con gcloud config get-value project.

    Se l'operazione va a buon fine, dovresti visualizzare un messaggio SUCCESSIVO 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

    1. Utilizza l'helper delle credenziali gcloud per autorizzare Docker a eseguire il push al tuo Container Registry.
      gcloud auth configure-docker
    2. Utilizza il plug-in Jib Maven per creare il container ed eseguirne il push su Container Registry.
      mvn compile jib:build -Dimage=gcr.io/PROJECT_ID/hello-service

    Dove PROJECT_ID è il tuo ID progetto Google Cloud. Puoi controllare il tuo ID progetto attuale con gcloud config get-value project.

    Se l'operazione ha esito positivo, dovresti visualizzare il messaggio COSTRUIRE RIUSCITA. L'immagine è archiviata in Container Registry e, se lo desideri, può essere riutilizzata.

  2. Esegui questo comando per eseguire il deployment della tua 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 del servizio Cloud Run. Nota che il deployment dell'immagine container è stato eseguito nel servizio e nella regione che hai configurato in precedenza in Configurazione di gcloud

    Rispondi y, "Sì", alla richiesta allow unauthenticated. Per ulteriori dettagli sull'autenticazione basata su IAM, consulta Gestione degli accessi.

    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.

Provalo

Prova il servizio per verificare di aver eseguito il deployment correttamente. Le richieste non dovrebbero andare a buon fine con un errore HTTP 500 o 503 (membri della classe Errori del server 5xx). Il tutorial illustra la risoluzione dei problemi relativi a questa risposta di errore.

Al servizio viene assegnato automaticamente un URL navigabile.

  1. Vai a questo URL con il browser web:

    1. Apri un browser web.

    2. Trova l'URL del servizio generato con il precedente comando di deployment.

      Se il comando di deployment non ha fornito un URL, si è verificato un problema. Esamina il messaggio di errore e intervieni di conseguenza: se non sono presenti indicazioni utili, consulta la guida alla risoluzione dei problemi ed eventualmente riprova a eseguire il comando di deployment.

    3. Accedi a questo URL copiandolo nella barra degli indirizzi del browser e premendo Invio.

  2. Visualizza l'errore HTTP 500 o HTTP 503.

    Se ricevi un errore HTTP 403, potresti aver rifiutato allow unauthenticated invocations alla richiesta di deployment. Concedi l'accesso non autenticato al servizio per risolvere il problema:

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

Per maggiori informazioni, consulta Consentire l'accesso pubblico (non autenticato).

Analisi del problema

Visualizza che l'errore HTTP 5xx riscontrato in precedenza nella sezione Prova è stato riscontrato come errore di runtime in produzione. Questo tutorial illustra un processo formale di gestione. Sebbene i processi di risoluzione degli errori di produzione siano ampiamente diversi, questo tutorial presenta una specifica sequenza di passaggi per mostrare l'applicazione di strumenti e tecniche utili.

Per analizzare questo problema, dovrai seguire queste fasi:

  • Raccogliere ulteriori dettagli sull'errore segnalato per supportare ulteriori indagini e impostare una strategia di mitigazione.
  • Allevia l’impatto sugli utenti decidendo di eseguire il push di una correzione o il rollback a una versione integro noto.
  • Riproduci l'errore per confermare che siano stati raccolti i dettagli corretti e che l'errore non si tratti di un problema
  • Esegui un'analisi delle cause principali del bug per trovare il codice, la configurazione o il processo che ha creato l'errore

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

Raccolta di ulteriori dettagli

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

Usa gli strumenti di osservabilità di Google Cloud disponibili per raccogliere ulteriori dettagli:

  1. Utilizza la console di Error Reporting, che fornisce una dashboard con dettagli e monitoraggio della ricorrenza degli errori con una traccia dello stack riconosciuta.

    Vai alla console di Error Reporting

    Screenshot dell&#39;elenco di errori, che include le colonne &quot;Stato risoluzione&quot;, Occorrenze, Errore e &quot;Visto in&quot;.
    Elenco degli errori registrati. Gli errori vengono raggruppati per messaggio per revisioni, servizi e piattaforme.
  2. Fai clic sull'errore per visualizzare i dettagli dell'analisi dello stack, notando le chiamate di funzione effettuate poco prima dell'errore.

    Screenshot di una singola analisi dello stack analizzata, che mostra un profilo comune dell&#39;errore.
    Il campo "Esempio di traccia dello stack" nella pagina dei dettagli dell'errore mostra una singola istanza dell'errore. Puoi esaminare ogni singola istanza.
  3. Utilizza Cloud Logging per esaminare la sequenza delle operazioni che hanno portato al problema, inclusi i messaggi di errore non inclusi nella console di Error Reporting a causa della mancanza di un'analisi dello stack di errori riconosciuta:

    Vai alla console di Cloud Logging

    Seleziona Revisione Cloud Run > hello-service dalla prima casella a discesa. Le voci di log verranno filtrate in base a quelle generate dal tuo servizio.

Scopri di più sulla visualizzazione dei log in Cloud Run

Esegui il rollback a una versione integra

Se si tratta di un servizio consolidato, il cui funzionamento è noto, verrà effettuata una revisione precedente del servizio su Cloud Run. Questo tutorial utilizza un nuovo servizio senza versioni precedenti, quindi non puoi eseguire un rollback.

Tuttavia, se hai un servizio con versioni precedenti di cui puoi eseguire il rollback, consulta Visualizzazione dei dettagli della revisione per estrarre il nome del container e i dettagli di configurazione necessari per creare un nuovo deployment funzionante del servizio.

Riproduzione dell'errore

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

Invia la stessa richiesta HTTP riprovandola e controlla se vengono segnalati lo stesso errore e i relativi dettagli. Potrebbe essere necessario attendere un po' di 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 di errori in produzione è sicura. Tuttavia, per molti servizi reali, questo non avviene: potresti dover riprodurre gli errori in un ambiente di test o limitare questo passaggio all'indagine locale.

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

Esecuzione dell'analisi delle cause principali

L'analisi delle cause principali è un passaggio importante per una risoluzione dei problemi efficace per assicurare la risoluzione del problema invece che di un sintomo.

In precedenza, in questo tutorial hai riprodotto il problema su Cloud Run, a conferma che il problema sia attivo quando il servizio è ospitato in Cloud Run. Ora riproduci il problema localmente per determinare se il problema è isolato dal 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, autenticala con gcloud:

    gcloud auth configure-docker

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

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

  3. Esegui il pull dell'immagine container da Container Registry al tuo ambiente. Questo passaggio potrebbe richiedere diversi minuti mentre scarica l'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 estrae un'immagine container se non ne esiste una sulla macchina locale.

  4. Esegui localmente per confermare che il problema non sia univoco per Cloud Run:

    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 per l'ascolto all'interno del container.
    • Il comando run avvia il container, ripristinando per impostazione predefinita il comando del punto di ingresso 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 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à alla modalità di accesso al servizio.
    • L'argomento finale gcr.io/PROJECT_ID/hello-service è un'immagine container tag, un'etichetta leggibile per l'identificatore hash sha256 di un'immagine container. Se non è disponibile localmente, docker tenta di recuperare l'immagine da un registro remoto.

    Nel browser, apri http://localhost:9000. Controlla se nell'output del terminale sono presenti messaggi di errore corrispondenti a quelli presenti su {ops_name}}.

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

    In questo caso, l'errore viene riprodotto localmente.

Ora che l'errore viene doppiamente confermato come persistente e causato dal codice del servizio invece che dalla piattaforma di hosting, è il momento di esaminare il codice più da vicino.

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

Rivedi l'analisi dello stack del report sugli errori e applica un controllo incrociato con il codice per trovare le righe specifiche in errore.

Node.js

Trova l'origine del messaggio di errore nel file index.js intorno al numero di riga richiamato 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

Trova l'origine del messaggio di errore nel file main.py intorno al numero di riga richiamato 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

Trova l'origine del messaggio di errore nel file main.go intorno al numero di riga richiamato 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

Trova l'origine del messaggio di errore nel file App.java intorno al numero di riga richiamato 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";
}

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

  • Viene registrato un errore nell'osservabilità 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, aggiungendo la dipendenza rigida su una variabile di ambiente, non includeva le modifiche correlate agli script di deployment e alla documentazione sui requisiti di runtime.

Correzione 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 in locale aggiungendo la variabile di ambiente:

      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 all'indirizzo http://localhost:9000

    3. Vedi "Hello Local World!" nella pagina

  • Modifica l'ambiente del servizio Cloud Run in esecuzione per includere questa variabile:

    1. Esegui il comando di aggiornamento dei servizi per aggiungere una variabile di ambiente:

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

  • Verifica che il servizio sia stato risolto:

    1. Vai all'URL del servizio Cloud Run nel browser.
    2. Vedi "Hello Override!" nella pagina.
    3. Verifica che non vengano visualizzati errori o messaggi imprevisti in Cloud Logging o Error Reporting.

Miglioramento della velocità futura della risoluzione dei problemi

In questo problema di produzione di esempio, l'errore riguardava la configurazione operativa. Esistono modifiche al codice che ridurranno al minimo l'impatto di questo problema in futuro.

  • Migliora il log degli errori per includere dettagli più specifici.
  • Invece di restituire un errore, imposta il servizio su un valore predefinito sicuro. Se l'utilizzo di un valore predefinito rappresenta una modifica alla normale funzionalità, 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 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 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 test in locale ricreando ed eseguendo il 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=$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=$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 le righe aggiuntive, ad esempio 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.

Ricerca di altri problemi nei log

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

Ad esempio, a volte i servizi Node.js restituiscono il seguente 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.

Risoluzione dei problemi di Terraform

Per la risoluzione dei problemi relativi a Terraform o per domande, consulta Risoluzione dei problemi di convalida dei criteri Terraform o contatta l'assistenza Terraform.

Esegui la pulizia

Se hai creato un nuovo progetto per questo tutorial, elimina il progetto. Se hai utilizzato un progetto esistente e vuoi conservarlo 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. Nella console Google Cloud, vai alla pagina Gestisci risorse.

    Vai a Gestisci risorse

  2. Nell'elenco dei progetti, seleziona il progetto che vuoi eliminare, quindi fai clic su Elimina.
  3. Nella finestra di dialogo, digita l'ID del progetto e fai clic su Chiudi per eliminare il progetto.

Eliminazione delle risorse del tutorial

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

  2. Rimuovi la configurazione della regione predefinita di gcloud aggiunta durante la configurazione del tutorial:

     gcloud config unset run/region
    
  3. Rimuovi la configurazione del progetto:

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

Passaggi successivi