Tutorial: Solución de problemas local de un servicio de Cloud Run

En este tutorial se muestra cómo puede solucionar problemas de un servicio de Cloud Run que no funciona un desarrollador de servicios mediante las herramientas de Google Cloud Observability 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.

Configurar los valores predeterminados de gcloud

Para configurar gcloud con los valores predeterminados de tu servicio de Cloud Run, sigue estos pasos:

  1. Configura tu proyecto predeterminado:

    gcloud config set project PROJECT_ID

    Sustituye PROJECT_ID por el nombre del proyecto que has creado para este tutorial.

  2. Configura gcloud para la región que hayas elegido:

    gcloud config set run/region REGION

    Sustituye REGION por la región de Cloud Run compatible que quieras.

Ubicaciones de Cloud Run

Cloud Run es regional, lo que significa que la infraestructura que ejecuta tus servicios de Cloud Run se encuentra en una región específica y Google la gestiona para que esté disponible de forma redundante en todas las zonas de esa región.

Cumplir tus requisitos de latencia, disponibilidad o durabilidad son factores primordiales para seleccionar la región en la que se ejecutan tus servicios de Cloud Run. Por lo general, puedes seleccionar la región más cercana a tus usuarios, pero debes tener en cuenta la ubicación de los otros Google Cloudproductos que utiliza tu servicio de Cloud Run. Usar Google Cloud productos juntos en varias ubicaciones puede afectar a la latencia y al coste de tu servicio.

Cloud Run está disponible en las siguientes regiones:

Con sujeción a los precios del nivel 1

  • asia-east1 (Taiwán)
  • asia-northeast1 (Tokio)
  • asia-northeast2 (Osaka)
  • asia-south1 (Bombay, la India)
  • europe-north1 (Finlandia) icono de una hoja CO2 bajo
  • europe-north2 (Estocolmo) icono de una hoja CO2 bajo
  • europe-southwest1 (Madrid) icono de una hoja CO2 bajo
  • europe-west1 (Bélgica) icono de una hoja CO2 bajo
  • europe-west4 (Países Bajos) icono de una hoja CO2 bajo
  • europe-west8 (Milán)
  • europe-west9 (París) icono de una hoja CO2 bajo
  • me-west1 (Tel Aviv)
  • northamerica-south1 (México)
  • us-central1 (Iowa) icono de una hoja CO2 bajo
  • us-east1 (Carolina del Sur)
  • us-east4 (Norte de Virginia)
  • us-east5 (Columbus)
  • us-south1 (Dallas) icono de una hoja CO2 bajo
  • us-west1 (Oregón) icono de una hoja CO2 bajo

Con sujeción a los precios del nivel 2

  • africa-south1 (Johannesburgo)
  • asia-east2 (Hong Kong)
  • asia-northeast3 (Seúl, Corea del Sur)
  • asia-southeast1 (Singapur)
  • asia-southeast2 (Yakarta)
  • asia-south2 (Delhi, la India)
  • australia-southeast1 (Sídney)
  • australia-southeast2 (Melbourne)
  • europe-central2 Varsovia (Polonia)
  • europe-west10 (Berlín)
  • europe-west12 (Turín)
  • europe-west2 (Londres, Reino Unido) icono de una hoja CO2 bajo
  • europe-west3 (Fráncfort, Alemania)
  • europe-west6 (Zúrich, Suiza) icono de una hoja Bajas emisiones de CO2
  • me-central1 (Doha)
  • me-central2 (Dammam)
  • northamerica-northeast1 (Montreal) icono de una hoja CO2 bajo
  • northamerica-northeast2 (Toronto) icono de una hoja CO2 bajo
  • southamerica-east1 (São Paulo, Brasil) icono de una hoja CO2 bajo
  • southamerica-west1 (Santiago, Chile) icono de una hoja CO2 bajo
  • us-west2 (Los Ángeles)
  • us-west3 (Salt Lake City)
  • us-west4 (Las Vegas)

Si ya has creado un servicio de Cloud Run, puedes ver la región en el panel de control de Cloud Run de la Google Cloud consola.

Ensamblar el código

Crea paso a paso un nuevo servicio de saludo de Cloud Run. 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. Para crear un directorio de hello-service, sigue estos pasos:

      mkdir hello-service
      cd hello-service
      
    2. Crea un proyecto de Node.js generando un archivo package.json:

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

      {
        "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": "^10.0.0",
          "google-auth-library": "^9.0.0",
          "got": "^11.5.0",
          "mocha": "^10.0.0"
        }
      }
      

    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. Para crear un directorio de hello-service, sigue estos pasos:

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

      go mod init example.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.cloudrun \
        -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: compilar una imagen de contenedor con Cloud Build, subir la imagen de contenedor a Container Registry y desplegar la imagen de contenedor en Cloud Run.

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 de Google Cloud. Puedes consultar el ID de tu proyecto actual con gcloud config get-value project.

    Si el proceso se completa correctamente, verás un mensaje de ÉXITO con 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 de Google Cloud. Puedes consultar el ID de tu proyecto actual con gcloud config get-value project.

    Si el proceso se completa correctamente, verás un mensaje de ÉXITO con 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 de Google Cloud. Puedes consultar el ID de tu proyecto actual con gcloud config get-value project.

    Si el proceso se completa correctamente, verás un mensaje de ÉXITO con 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

    1. Usa el asistente de credenciales de gcloud para autorizar a Docker a enviar contenido a tu Container Registry.
      gcloud auth configure-docker
    2. Usa el complemento Jib Maven para compilar y enviar el contenedor a Container Registry.
      mvn compile jib:build -Dimage=gcr.io/PROJECT_ID/hello-service

    Donde PROJECT_ID es el ID de tu proyecto de 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 de Google Cloud. hello-service es el nombre de la imagen de contenedor y el nombre del servicio de Cloud Run. Ten en cuenta que la imagen del contenedor se despliega en el servicio y la región que configuraste anteriormente en Configurar gcloud.

    Responde y ("Sí") a la petición permitir sin autenticar. Consulta Gestionar el acceso para obtener más información sobre la autenticación basada en IAM.

    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 la función

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 5xx Server errors). En el tutorial se explica cómo solucionar este error.

Al servicio se le asigna automáticamente una URL navegable.

  1. Ve a esta URL con tu navegador web:

    1. Abrir un navegador web

    2. Busca la URL del servicio que ha generado el comando de implementación anterior.

      Si el comando de implementación no ha proporcionado una URL, significa que algo ha ido mal. Revisa el mensaje de error y actúa en consecuencia. Si no hay ninguna guía práctica, consulta la guía para solucionar problemas y, si es necesario, vuelve a intentar ejecutar el comando de implementación.

    3. Para ir a esta URL, cópiala en la barra de direcciones del navegador y pulsa INTRO.

  2. Consulta el error HTTP 500 o HTTP 503.

    Si recibes un error HTTP 403, es posible que hayas rechazado allow unauthenticated invocations en la solicitud de implementación. Concede acceso público al servicio para solucionar este problema:

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

Para obtener más información, consulta Permitir el acceso público (sin autenticación).

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 para resolver 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 de Google Cloud Observability disponibles para obtener más detalles:

  1. Usa la consola Error Reporting, que proporciona un panel de control con detalles y seguimiento de la recurrencia de los errores con un rastreo de pila reconocido.

    Ir a la consola Error Reporting

    Captura de pantalla de la lista de errores, incluidas las columnas &quot;Estado de resolución&quot;, &quot;Ocurrencias&quot;, &quot;Error&quot; y &quot;Visto en&quot;.
    Lista de errores registrados. Los errores se agrupan por mensaje en las revisiones, los servicios y las plataformas.
  2. Haz clic en el error para ver los detalles del seguimiento de pila y anota las llamadas de función que se han hecho justo antes del error.

    Captura de pantalla de un único rastreo de pila analizado que muestra un perfil habitual de este error.
    La sección "Ejemplo de rastreo de la pila" de la página de detalles del error muestra una sola instancia del error. Puedes revisar cada una de las instancias.
  3. Usa Cloud Logging para revisar la secuencia de operaciones que han llevado al problema, incluidos los mensajes de error que no se incluyen en la consola Error Reporting porque no se ha reconocido ningún seguimiento de pila de errores:

    Ir a la consola de Cloud Logging

    Selecciona Revisión de Cloud Run > hello-service en el primer cuadro desplegable. De esta forma, se filtrarán las entradas de registro para mostrar solo las que ha generado tu servicio.

Consulta más información sobre cómo ver registros en Cloud Run.

Restaurar una versión correcta

Si se trata de un servicio establecido que funciona, habrá una revisión anterior del servicio en Cloud Run. En este tutorial se usa un servicio nuevo sin versiones anteriores, por lo que no puedes restaurar una versión anterior.

Sin embargo, si tienes un servicio con versiones anteriores a las que puedes volver, sigue los pasos que se indican en Ver los detalles de una revisión para extraer el nombre del contenedor y los detalles de configuración necesarios para crear una nueva implementación operativa de tu servicio.

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.

Anteriormente en este tutorial, has reproducido el problema en Cloud Run, lo que confirma que el problema está activo cuando el servicio está alojado en Cloud Run. 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 código localmente para confirmar que el problema no es exclusivo de Cloud Run:

    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 imagen de contenedor tag, una etiqueta legible por humanos para el identificador de hash sha256 de una imagen de 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 del terminal para ver si hay mensajes de error que coincidan con los de {ops_name}}.

    Si el problema no se puede reproducir de forma local, puede que sea exclusivo del entorno de Cloud Run. Consulta la guía de solución de problemas de Cloud Run para ver áreas específicas que puedes 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.

Vuelve a consultar el rastreo de pila del informe de errores y compáralo con el código para encontrar las líneas específicas que fallan.

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 en torno al 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 Cloud Run en ejecución para incluir esta variable:

    1. Ejecuta el comando de actualización de servicios para añadir una variable de entorno:

      gcloud run services update hello-service \
        --set-env-vars NAME=Override
      
    2. Espera unos segundos mientras Cloud Run 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 Cloud Run.
    2. Verás el mensaje "Hello Override!" en la página.
    3. Comprueba que no aparezcan mensajes ni errores inesperados en Cloud Logging o Error Reporting.

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=$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=$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.

Solución de problemas de Terraform

Si tienes alguna duda o necesitas ayuda para solucionar problemas relacionados con Terraform, consulta la sección Solución de problemas de validación de políticas de Terraform o ponte en contacto con el equipo de Asistencia de Terraform.