Solucionar problemas locales


En este tutorial se muestra cómo solucionar problemas de un servicio de Knative Serving que no funciona correctamente mediante las herramientas de Stackdriver para la detección y un flujo de trabajo de desarrollo local para la investigación.

Esta guía detallada, que complementa la guía para solucionar problemas, utiliza un proyecto de ejemplo que genera errores de tiempo de ejecución al implementarse. Tu tarea será solucionar estos errores para encontrar y corregir el problema.

Objetivos

  • Escribir, compilar y desplegar un servicio en el servicio de Knative
  • Usar Cloud Logging para identificar un error
  • Recuperar la imagen de contenedor de Container Registry para analizar la causa raíz
  • Corregir el servicio "production" y, a continuación, mejorarlo para mitigar problemas futuros

Costes

En este documento, se utilizan los siguientes componentes facturables de Google Cloud:

Para generar una estimación de costes basada en el uso previsto, utiliza la calculadora de precios.

Los usuarios nuevos Google Cloud pueden disfrutar de una prueba gratuita.

Antes de empezar

Ensamblar el código

Crea un nuevo servicio de saludo de Knative paso a paso. Te recordamos que este servicio crea un error de tiempo de ejecución a propósito para el ejercicio de solución de problemas.

  1. Para crear un proyecto:

    Node.js

    Crea un proyecto de Node.js definiendo el paquete de servicio, las dependencias iniciales y algunas operaciones comunes.

    1. Crea un directorio hello-service:

      mkdir hello-service
      cd hello-service
      
    2. Generar un archivo package.json:

      npm init --yes
      npm install express@4
      
    3. Abre el nuevo archivo package.json en tu editor y configura una secuencia de comandos start para ejecutar node index.js. Cuando haya terminado, el archivo tendrá este aspecto:

      {
        "name": "hello-service",
        "version": "1.0.0",
        "description": "",
        "main": "index.js",
        "scripts": {
            "start": "node index.js",
            "test": "echo \"Error: no test specified\" && exit 1"
        },
        "keywords": [],
        "author": "",
        "license": "ISC",
        "dependencies": {
            "express": "^4.17.1"
        }
      }

    Si sigues desarrollando este servicio más allá del tutorial inmediato, te recomendamos que rellenes la descripción y el autor, y que evalúes la licencia. Para obtener más información, consulta la documentación de package.json.

    Python

    1. Para crear un directorio de hello-service, sigue estos pasos:

      mkdir hello-service
      cd hello-service
      
    2. Crea un archivo requirements.txt y copia tus dependencias en él:

      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
      

    Go

    1. Crea un directorio hello-service:

      mkdir hello-service
      cd hello-service
      
    2. Crea un proyecto de Go inicializando un nuevo módulo de Go:

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

    Puedes actualizar el nombre específico como quieras. Debes actualizar el nombre si el código se publica en un repositorio de código accesible a través de la Web.

    Java

    1. Crea un proyecto de Maven:

      mvn archetype:generate \
        -DgroupId=com.example \
        -DartifactId=hello-service \
        -DarchetypeArtifactId=maven-archetype-quickstart \
        -DinteractiveMode=false
      
    2. Copia las dependencias en la lista de dependencias pom.xml (entre los elementos <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 el ajuste de compilación en tu pom.xml (en los elementos <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 servicio HTTP para gestionar las solicitudes entrantes:

    Node.js

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

    Python

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

    Go

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

    Java

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

  3. Crea un Dockerfile para definir la imagen de contenedor que se usará para desplegar el servicio:

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

    En este ejemplo se usa Jib para crear imágenes de Docker con herramientas comunes de Java. Jib optimiza las compilaciones de contenedores sin necesidad de usar un Dockerfile ni de tener Docker instalado. Más información sobre cómo crear contenedores 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>
    

Envío del código

El envío de código consta de tres pasos: crear una imagen de contenedor con Cloud Build, subir la imagen de contenedor a Container Registry y desplegar la imagen de contenedor en Knative Serving.

Para enviar tu código, sigue estos pasos:

  1. Crea el contenedor y publícalo en Container Registry:

    Node.js

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

    Donde PROJECT_ID es el ID de tu proyecto. Google Cloud Puedes consultar el ID de tu proyecto actual con gcloud config get-value project.

    Si la operación se realiza correctamente, debería aparecer un mensaje SUCCESS que contenga el ID, la hora de creación y el nombre de la imagen. La imagen se almacena en Container Registry y se puede volver a usar si quieres.

    Python

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

    Donde PROJECT_ID es el ID de tu proyecto. Google Cloud Puedes consultar el ID de tu proyecto actual con gcloud config get-value project.

    Si la operación se realiza correctamente, debería aparecer un mensaje SUCCESS que contenga el ID, la hora de creación y el nombre de la imagen. La imagen se almacena en Container Registry y se puede volver a usar si quieres.

    Go

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

    Donde PROJECT_ID es el ID de tu proyecto. Google Cloud Puedes consultar el ID de tu proyecto actual con gcloud config get-value project.

    Si la operación se realiza correctamente, debería aparecer un mensaje SUCCESS que contenga el ID, la hora de creación y el nombre de la imagen. La imagen se almacena en Container Registry y se puede volver a usar si quieres.

    Java

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

    Donde PROJECT_ID es el ID de tu proyecto. Google Cloud Puedes consultar el ID de tu proyecto actual con gcloud config get-value project.

    Si todo va bien, debería aparecer el mensaje BUILD SUCCESS. La imagen se almacena en Container Registry y se puede volver a usar si quieres.

  2. Ejecuta el siguiente comando para implementar tu aplicación:

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

    Sustituye PROJECT_ID por el ID de tu proyecto. Google Cloud hello-service es tanto el nombre de la imagen del contenedor como el nombre del servicio de Knative Serving. Verás que la imagen del contenedor se ha desplegado en el servicio y el clúster que configuraste anteriormente en Configurar gcloud.

    Espera a que se complete el despliegue, que puede tardar aproximadamente medio minuto. Si la acción se realiza correctamente, la línea de comandos mostrará la URL del servicio.

Probar

Prueba el servicio para confirmar que lo has implementado correctamente. Las solicitudes deben fallar con un error HTTP 500 o 503 (miembros de la clase Errores del servidor 5xx). En el tutorial se explica cómo solucionar este error.

Si tu clúster está configurado con un dominio predeterminado enrutable, omite los pasos anteriores y copia la URL en tu navegador web.

Si no usas certificados TLS automáticos ni asignación de dominio, no se te proporcionará una URL navegable para tu servicio.

En su lugar, usa la URL proporcionada y la dirección IP de la puerta de enlace de entrada del servicio para crear un comando curl que pueda enviar solicitudes a tu servicio:

  1. Para obtener la IP externa del balanceador de carga, ejecuta el siguiente comando:

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

    Sustituye ASM-INGRESS-NAMESPACE por el espacio de nombres en el que se encuentra tu entrada de Cloud Service Mesh. Especifica istio-system si has instalado Cloud Service Mesh con su configuración predeterminada.

    La salida resultante tiene un aspecto similar al siguiente:

    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

    donde el valor EXTERNAL-IP es la dirección IP externa del balanceador de carga.

  2. Ejecuta un comando curl con esta dirección GATEWAY_IP en la URL.

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

    Sustituye SERVICE-DOMAIN por el dominio asignado de forma predeterminada a tu servicio. Para obtenerla, toma la URL predeterminada y elimina el protocolo http://.

  3. Aparece el mensaje de error HTTP 500 o HTTP 503.

Investigar el problema

Imagina que el error HTTP 5xx que se ha producido en la sección Probar de arriba se ha producido como un error de tiempo de ejecución en producción. En este tutorial se explica un proceso formal para gestionarlo. Aunque los procesos de resolución de errores de producción varían mucho, en este tutorial se presenta una secuencia de pasos concreta para mostrar la aplicación de herramientas y técnicas útiles.

Para investigar este problema, tendrás que completar las siguientes fases:

  • Recoge más detalles sobre el error notificado para poder investigar más a fondo y definir una estrategia de mitigación.
  • Reduce el impacto en los usuarios decidiendo si quieres seguir adelante con una corrección o volver a una versión que funcione correctamente.
  • Reproduce el error para confirmar que se han recogido los detalles correctos y que no se trata de un fallo puntual.
  • Realiza un análisis de la causa raíz del error para encontrar el código, la configuración o el proceso que lo ha provocado.

Al principio de la investigación, tienes una URL, una marca de tiempo y el mensaje "Error interno del servidor".

Gathering further details

Recaba más información sobre el problema para saber qué ha ocurrido y determinar los pasos siguientes.

Usa las herramientas disponibles para obtener más detalles:

  1. Consulta los registros para obtener más información.

  2. Usa Cloud Logging para revisar la secuencia de operaciones que han provocado el problema, incluidos los mensajes de error.

Restaurar una versión correcta

Si tienes una revisión que sabes que funcionaba, puedes restaurar tu servicio para usar esa revisión. Por ejemplo, no podrás revertir el servicio hello-service que has implementado en este tutorial porque solo contiene una revisión.

Para localizar una revisión y restaurar tu servicio, sigue estos pasos:

  1. Lista de todas las revisiones de tu servicio.

  2. Migrar todo el tráfico a la revisión en buen estado.

Reproducir el error

Con los detalles que has obtenido anteriormente, confirma que el problema se produce de forma constante en condiciones de prueba.

Envía la misma solicitud HTTP probándola de nuevo y comprueba si se informa del mismo error y los mismos detalles. Los detalles del error pueden tardar un poco en aparecer.

Como el servicio de ejemplo de este tutorial es de solo lectura y no activa ningún efecto secundario que complique las cosas, es seguro reproducir errores en producción. Sin embargo, en muchos servicios reales no será así: es posible que tengas que reproducir errores en un entorno de prueba o limitar este paso a una investigación local.

Reproducir el error establece el contexto para seguir trabajando. Por ejemplo, si los desarrolladores no pueden reproducir el error, es posible que se necesite una instrumentación adicional del servicio para investigar más a fondo.

Llevar a cabo un análisis de causas

El análisis de la causa raíz es un paso importante para solucionar problemas de forma eficaz y asegurarte de que resuelves el problema en lugar de un síntoma.

En una sección anterior de este tutorial, has reproducido el problema en el servicio de Knative, lo que confirma que el problema está activo cuando el servicio se aloja en el servicio de Knative. Ahora, reproduce el problema de forma local para determinar si se limita al código o si solo se produce en el alojamiento de producción.

  1. Si no has usado Docker CLI de forma local con Container Registry, autentícalo con gcloud:

    gcloud auth configure-docker

    Para ver otras opciones, consulta Métodos de autenticación de Container Registry.

  2. Si el nombre de la imagen de contenedor usada más recientemente no está disponible, la descripción del servicio tiene la información de la imagen de contenedor implementada más recientemente:

    gcloud run services describe hello-service

    Busca el nombre de la imagen del contenedor en el objeto spec. Un comando más específico puede obtenerlo directamente:

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

    Este comando muestra el nombre de una imagen de contenedor, como gcr.io/PROJECT_ID/hello-service.

  3. Extrae la imagen de contenedor de Container Registry a tu entorno. Este paso puede tardar varios minutos, ya que se descarga la imagen de contenedor:

    docker pull gcr.io/PROJECT_ID/hello-service

    Las actualizaciones posteriores de la imagen de contenedor que reutilicen este nombre se podrán recuperar con el mismo comando. Si te saltas este paso, el comando docker run de abajo extraerá una imagen de contenedor si no hay ninguna en la máquina local.

  4. Ejecuta el comando de forma local para confirmar que el problema no es exclusivo de Knative Serving:

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

    Desglosando los elementos del comando anterior,

    • El servicio usa la variable de entorno PORT para determinar el puerto en el que debe escuchar dentro del contenedor.
    • El comando run inicia el contenedor. De forma predeterminada, se usa el comando de punto de entrada definido en el Dockerfile o en una imagen de contenedor principal.
    • La marca --rm elimina la instancia del contenedor al salir.
    • La marca -e asigna un valor a una variable de entorno. -e PORT=$PORT propaga la variable PORT del sistema local al contenedor con el mismo nombre de variable.
    • La marca -p publica el contenedor como un servicio disponible en localhost en el puerto 9000. Las solicitudes a localhost:9000 se enrutarán al contenedor en el puerto 8080. Esto significa que la salida del servicio sobre el número de puerto en uso no coincidirá con la forma en que se accede al servicio.
    • El último argumento, gcr.io/PROJECT_ID/hello-service , es una ruta de repositorio que apunta a la versión más reciente de la imagen del contenedor. Si no está disponible localmente, Docker intenta obtener la imagen de un registro remoto.

    En el navegador, abre http://localhost:9000. Comprueba la salida de la terminal para ver si hay mensajes de error que coincidan con los de Google Cloud Observability.

    Si el problema no se puede reproducir de forma local, puede que sea exclusivo del entorno de servicio de Knative. Consulta la guía para solucionar problemas de Knative Serving para saber qué áreas concretas debes investigar.

    En este caso, el error se reproduce de forma local.

Ahora que se ha confirmado que el error persiste y que lo provoca el código de servicio en lugar de la plataforma de alojamiento, es el momento de investigar el código más a fondo.

Para los fines de este tutorial, es seguro asumir que el código del contenedor y el código del sistema local son idénticos.

Node.js

Busca el origen del mensaje de error en el archivo index.js alrededor del número de línea que se indica en el seguimiento de pila que se muestra en los registros:
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

Busca el origen del mensaje de error en el archivo main.py alrededor del número de línea que se indica en el seguimiento de pila que se muestra en los registros:
NAME = os.getenv("NAME")

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

Go

Busca el origen del mensaje de error en el archivo main.go alrededor del número de línea que se indica en el seguimiento de pila que se muestra en los registros:

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

Busca el origen del mensaje de error en el archivo App.java alrededor del número de línea indicado en el seguimiento de pila que se muestra en los registros:

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

Al examinar este código, se llevan a cabo las siguientes acciones cuando no se define la variable de entorno NAME:

  • Se registra un error en Google Cloud Observability
  • Se envía una respuesta de error HTTP

El problema se debe a que falta una variable, pero la causa principal es más específica: el cambio de código que añade la dependencia fija de una variable de entorno no incluye los cambios relacionados en los scripts de implementación ni en la documentación de los requisitos de tiempo de ejecución.

Corregir la causa principal

Ahora que hemos recogido el código y hemos identificado la posible causa principal, podemos tomar medidas para solucionarlo.

  • Comprueba si el servicio funciona localmente con el entorno NAME disponible:

    1. Ejecuta el contenedor de forma local con la variable de entorno añadida:

      PORT=8080 && docker run --rm -e PORT=$PORT -p 9000:$PORT \
       -e NAME="Local World!" \
       gcr.io/PROJECT_ID/hello-service
    2. En el navegador, ve a http://localhost:9000.

    3. Verás el mensaje "Hello Local World!" en la página.

  • Modifica el entorno del servicio de Knative en ejecución para incluir esta variable:

    1. Ejecuta el comando de actualización de servicios con el parámetro --update-env-vars para añadir una variable de entorno:

      gcloud run services update hello-service \
        --update-env-vars NAME=Override
      
    2. Espera unos segundos mientras Knative Serving crea una nueva revisión basada en la revisión anterior con la nueva variable de entorno añadida.

  • Confirma que el servicio ya funciona:

    1. En el navegador, ve a la URL del servicio de Knative.
    2. Verás el mensaje "Hello Override!" en la página.
    3. Comprueba que no aparezcan mensajes ni errores inesperados en Cloud Logging.

Mejorar la velocidad de solución de problemas en el futuro

En este ejemplo de problema de producción, el error estaba relacionado con la configuración operativa. Se han realizado cambios en el código para minimizar el impacto de este problema en el futuro.

  • Mejorar el registro de errores para que incluya detalles más específicos.
  • En lugar de devolver un error, haz que el servicio vuelva a un valor predeterminado seguro. Si el uso de un valor predeterminado representa un cambio en la funcionalidad normal, utiliza un mensaje de advertencia para monitorizarlo.

Vamos a eliminar la variable de entorno NAME como dependencia obligatoria.

  1. Quita el código de gestión de 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. Añade un código nuevo que defina un valor alternativo:

    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. Prueba de forma local volviendo a compilar y ejecutar el contenedor en los casos de configuración afectados:

    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

    Comprueba que la variable de entorno NAME sigue funcionando:

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

    Confirma que el servicio funciona sin la variable NAME:

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

    Si el servicio no devuelve ningún resultado, confirma que al eliminar el código en el primer paso no se han eliminado líneas adicionales, como las que se usan para escribir la respuesta.

  4. Para ello, vuelve a la sección Implementar el código.

    Cada despliegue en un servicio crea una revisión nueva y empieza a servir tráfico automáticamente cuando está listo.

    Para borrar las variables de entorno definidas anteriormente, sigue estos pasos:

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

Añade la nueva función del valor predeterminado a la cobertura de pruebas automatizadas del servicio.

Buscar otros problemas en los registros

Es posible que veas otros problemas en el visualizador de registros de este servicio. Por ejemplo, una llamada al sistema no compatible aparecerá en los registros como "Container Sandbox Limitation".

Por ejemplo, los servicios de Node.js a veces generan este mensaje de registro:

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.

En este caso, la falta de compatibilidad no afecta al servicio de ejemplo hello-service.

Limpieza

Puedes eliminar los recursos creados para este tutorial para evitar que se te cobren.

Eliminar recursos del tutorial

  1. Elimina el servicio de Knative que has desplegado en este tutorial:

    gcloud run services delete SERVICE-NAME

    Donde SERVICE-NAME es el nombre del servicio que has elegido.

    También puedes eliminar servicios de Knative Serving desde la consola deGoogle Cloud :

    Ir a Knative serving

  2. Elimina las configuraciones predeterminadas de gcloud que has añadido durante la configuración del tutorial:

     gcloud config unset run/platform
     gcloud config unset run/cluster
     gcloud config unset run/cluster_location
    
  3. Elimina la configuración del proyecto:

     gcloud config unset project
    
  4. Elimina otros recursos de Google Cloud que hayas creado en este tutorial:

Siguientes pasos