Solução de problemas local


Neste tutorial, mostramos como solucionar problemas de um serviço de exibição do Knative corrompido usando ferramentas do Stackdriver para descoberta e um fluxo de trabalho de desenvolvimento local para investigação.

Neste "tutorial de estudo de caso" passo a passo do guia de solução de problemas é usado um projeto de amostra que resulta em erros de ambiente de execução quando implantados, que você soluciona para encontrar e corrigir o problema.

Objetivos

  • Gravar, criar e implantar um serviço na exibição do Knative
  • Usar o Cloud Logging para identificar um erro
  • Recuperar a imagem do contêiner do Container Registry para uma análise de causa raiz
  • Corrigir o serviço de "produção" e melhorá-lo para reduzir problemas futuros

Custos

Neste documento, você usará os seguintes componentes faturáveis do Google Cloud:

Para gerar uma estimativa de custo baseada na projeção de uso deste tutorial, use a calculadora de preços. Novos usuários do Google Cloud podem estar qualificados para uma avaliação gratuita.

Antes de começar

Como montar o código

Crie um novo serviço completo de veiculação do Knative em detalhes. Lembre-se de que esse serviço cria um erro de ambiente de execução de propósito para o exercício de solução de problemas.

  1. Crie um novo projeto:

    Node.js

    Crie um projeto Node.js definindo o pacote de serviços, as dependências iniciais e algumas operações comuns.

    1. Criar um diretório hello-service:

      mkdir hello-service
      cd hello-service
      
    2. Gere um arquivo package.json:

      npm init --yes
      npm install --save express@4
      
    3. Abra o novo arquivo package.json no seu editor e configure um script start para executar node index.js. Quando terminar, o arquivo terá esta aparência:

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

    Se você continuar a evoluir esse serviço além do tutorial imediato, preencha a descrição, o autor e avalie a licença. Para mais detalhes, leia a documentação do package.json.

    Python

    1. Crie um novo diretório hello-service:

      mkdir hello-service
      cd hello-service
      
    2. Crie um arquivo requirements.txt e copie suas dependências para ele:

      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==20.1.0
      Werkzeug==3.0.1
      

    Go

    1. Criar um diretório hello-service:

      mkdir hello-service
      cd hello-service
      
    2. Crie um projeto em Go inicializando um novo módulo go:

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

    É possível atualizar o nome específico como você quiser: atualize o nome se o código for publicado em um repositório de código acessível pela web.

    Java

    1. Criar um projeto Maven:

      mvn archetype:generate \
        -DgroupId=com.example \
        -DartifactId=hello-service \
        -DarchetypeArtifactId=maven-archetype-quickstart \
        -DinteractiveMode=false
      
    2. Copie as dependências para sua lista de dependências pom.xml (entre os 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. Copie a configuração de build para o pom.xml (nos 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. Crie um serviço HTTP para lidar com solicitações de entrada:

    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. Crie um Dockerfile para definir a imagem do contêiner usada para implantar o serviço:

    Node.js

    
    # Use the official lightweight Node.js image.
    # https://hub.docker.com/_/node
    FROM node:18-slim
    
    # Create and change to the app directory.
    WORKDIR /usr/src/app
    
    # Copy application dependency manifests to the container image.
    # A wildcard is used to ensure copying both package.json AND package-lock.json (when available).
    # Copying this first prevents re-running npm install on every code change.
    COPY package*.json ./
    
    # Install production dependencies.
    # If you add a package-lock.json, speed your build by switching to 'npm ci'.
    # RUN npm ci --only=production
    RUN npm install --only=production
    
    # Copy local code to the container image.
    COPY . ./
    
    # Run the web service on container startup.
    CMD [ "npm", "start" ]
    

    Python

    
    # Use the official Python image.
    # https://hub.docker.com/_/python
    FROM python:3.11
    
    # Allow statements and log messages to immediately appear in the Cloud Run logs
    ENV PYTHONUNBUFFERED True
    
    # Copy application dependency manifests to the container image.
    # Copying this separately prevents re-running pip install on every code change.
    COPY requirements.txt ./
    
    # Install production dependencies.
    RUN pip install -r requirements.txt
    
    # Copy local code to the container image.
    ENV APP_HOME /app
    WORKDIR $APP_HOME
    COPY . ./
    
    # Run the web service on container startup.
    # Use gunicorn webserver with one worker process and 8 threads.
    # For environments with multiple CPU cores, increase the number of workers
    # to be equal to the cores available.
    # Timeout is set to 0 to disable the timeouts of the workers to allow Cloud Run to handle instance scaling.
    CMD exec gunicorn --bind :$PORT --workers 1 --threads 8 --timeout 0 main:app
    

    Go

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

    Esta amostra usa o Jib (em inglês) para criar imagens do Docker usando ferramentas comuns do Java. O Jib otimiza builds de contêiner sem a necessidade de um Dockerfile ou de ter o Docker (em inglês) instalado. Saiba mais sobre como criar contêineres Java com o 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>
    

Como enviar o código

O código de envio consiste em três etapas: criar uma imagem de contêiner com o Cloud Build, fazer upload da imagem do contêiner para o Container Registry e implantar a imagem de contêiner na exibição do Knative.

Para enviar o código, siga estas etapas:

  1. Compile seu contêiner e publique no Container Registry.

    Node.js

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

    em que PROJECT_ID é o ID do projeto no Google Cloud. Verifique o ID do projeto atual com gcloud config get-value project.

    Após a conclusão, você verá uma mensagem de "SUCESSO" contendo o ID, a hora da criação e o nome da imagem. A imagem é armazenada no Container Registry e poderá ser reutilizada, se você quiser.

    Python

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

    em que PROJECT_ID é o ID do projeto no Google Cloud. Verifique o ID do projeto atual com gcloud config get-value project.

    Após a conclusão, você verá uma mensagem de "SUCESSO" contendo o ID, a hora da criação e o nome da imagem. A imagem é armazenada no Container Registry e poderá ser reutilizada, se você quiser.

    Go

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

    em que PROJECT_ID é o ID do projeto no Google Cloud. Verifique o ID do projeto atual com gcloud config get-value project.

    Após a conclusão, você verá uma mensagem de "SUCESSO" contendo o ID, a hora da criação e o nome da imagem. A imagem é armazenada no Container Registry e poderá ser reutilizada, se você quiser.

    Java

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

    em que PROJECT_ID é o ID do projeto no Google Cloud. Verifique o ID do projeto atual com gcloud config get-value project.

    Após a conclusão, você verá uma mensagem "BUILD SUCCESS". A imagem é armazenada no Container Registry e poderá ser reutilizada, se você quiser.

  2. Execute o comando a seguir para implantar o app:

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

    Substitua PROJECT_ID pelo ID do projeto do Google Cloud. hello-service é o nome da imagem do contêiner e do serviço de veiculação do Knative. Observe que a imagem do contêiner é implantada no serviço e no cluster que você configurou anteriormente em Como configurar o gcloud.

    Aguarde até que a implantação esteja concluída. Isso pode levar cerca de 30 segundos. Em caso de sucesso, a linha de comando exibe o URL de serviço.

Testar

Teste o serviço para confirmar que o implantou. As solicitações falham com um erro HTTP 500 ou 503 (membros da classe Erros de servidor 5xx). O tutorial explica a solução de problemas dessa resposta de erro.

Se o cluster estiver configurado com um domínio padrão roteável, ignore as etapas acima e copie o URL para o navegador da Web.

Se você não usa certificados TLS automáticos e mapeamento de domínio, não recebe um URL navegável para seu serviço.

Em vez disso, use o URL fornecido e o endereço IP do gateway de entrada do serviço para criar um comando curl que possa fazer solicitações ao seu serviço:

  1. Para conseguir o IP externo do balanceador de carga, execute o seguinte comando:

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

    Substitua ASM-INGRESS-NAMESPACE pelo namespace em que a entrada do Anthos Service Mesh está localizada. Especifique istio-system se você instalou o Anthos Service Mesh usando a configuração padrão.

    A resposta resultante será semelhante a:

    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
    

    em que o valor EXTERNAL-IP é o endereço IP externo do balanceador de carga.

  2. Execute um comando curl usando este endereço GATEWAY_IP no URL.

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

    Substitua SERVICE-DOMAIN pelo domínio padrão atribuído ao seu serviço. Para isso, use o URL padrão e remova o protocolo http://.

  3. Veja a mensagem de erro HTTP 500 ou HTTP 503.

Como investigar o problema

Veja que o erro HTTP 5xx encontrado acima em Como testar foi encontrado como um erro de ambiente de execução de produção. Este tutorial aborda um processo formal para lidar com ele. Embora os processos de resolução de erros de produção variem muito, este tutorial apresenta uma sequência específica de etapas para mostrar a aplicação de ferramentas e técnicas úteis.

Para investigar esse problema, siga estas etapas:

  • Colete mais detalhes sobre o erro relatado para ajudar na investigação e definir uma estratégia de mitigação.
  • Alivie o impacto do usuário decidindo avançar em uma correção ou reversão para uma versão íntegra conhecida.
  • Reproduza o erro para confirmar se os detalhes corretos foram coletados e se o erro não é uma falha única.
  • Realize uma análise de causa-raiz no bug para encontrar o código, a configuração ou o processo que criou o erro.

No início da investigação, há um URL, um carimbo de data/hora e a mensagem "Erro interno do servidor".

Como coletar mais detalhes

Reúna mais informações sobre o problema para entender o que aconteceu e determinar as próximas etapas.

Use as ferramentas disponíveis para coletar mais detalhes:

  1. Veja os registros para mais detalhes.

  2. Use o Cloud Logging para analisar a sequência de operações que levam ao problema, incluindo mensagens de erro.

Reverter para uma versão íntegra

Se tiver uma revisão que você sabe que estava funcionando, poderá reverter seu serviço para usar essa revisão. Por exemplo, não será possível fazer uma reversão no novo serviço hello-service implantado neste tutorial, porque ele contém apenas uma revisão.

Para localizar uma revisão e reverter o serviço:

  1. Liste todas as revisões do serviço.

  2. Migre todo o tráfego para a revisão íntegra.

Como reproduzir o erro

Usando os detalhes que você recebeu anteriormente, confirme se o problema ocorre de forma consistente nas condições de teste.

Envie a mesma solicitação HTTP testando-a novamente e veja se o mesmo erro e detalhes são relatados. Pode levar algum tempo para que os detalhes do erro sejam exibidos.

Como o serviço de amostra neste tutorial é somente leitura e não aciona efeitos secundários de complicações, a reprodução de erros na produção é segura. No entanto, para muitos serviços reais, isso não acontecerá: talvez seja necessário reproduzir erros em um ambiente de teste ou limitar essa etapa à investigação local.

A reprodução do erro estabelece o contexto para trabalhos futuros. Por exemplo, se os desenvolvedores não conseguirem reproduzir o erro, investigações adicionais podem exigir instrumentação adicional do serviço.

Como realizar uma análise de causa raiz

A análise da causa raiz é uma etapa importante na solução de problemas eficaz para garantir que você corrija o problema em vez do sintoma.

Anteriormente neste tutorial, você reproduziu o problema na exibição do Knative, o que confirma que o problema está ativo quando o serviço está hospedado na disponibilização do Knative. Agora, reproduza o problema localmente para determinar se ele está isolado do código ou se ele só aparece na hospedagem de produção.

  1. Se você não tiver usado a CLI do Docker localmente com o Container Registry, faça a autenticação com o gcloud:

    gcloud auth configure-docker

    Para abordagens alternativas, consulte Métodos de autenticação do Container Registry.

  2. Se o nome da imagem do contêiner usado mais recentemente não estiver disponível, a descrição do serviço terá as informações da imagem do contêiner implantada mais recentemente:

    gcloud run services describe hello-service

    Encontre o nome da imagem do contêiner dentro do objeto spec. Um comando mais segmentado pode recuperá-lo diretamente:

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

    Esse comando revela um nome de imagem de contêiner como gcr.io/PROJECT_ID/hello-service.

  3. Extraia a imagem do contêiner do Container Registry para seu ambiente. Essa etapa pode levar alguns minutos para fazer o download da imagem do contêiner:

    docker pull gcr.io/PROJECT_ID/hello-service

    Atualizações posteriores na imagem do contêiner que reutilizam esse nome podem ser recuperadas com o mesmo comando. Se você pular esta etapa, o comando docker run abaixo extrairá uma imagem de contêiner se ela não estiver presente na máquina local.

  4. Execute localmente para confirmar se o problema não é exclusivo da veiculação do Knative:

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

    Analisando os elementos do comando acima:

    • A variável de ambiente PORT é usada pelo serviço para determinar a porta a ser ouvida no contêiner.
    • O comando run inicia o contêiner, assumindo como padrão o comando entrypoint definido no Dockerfile ou em uma imagem de contêiner pai.
    • A sinalização --rm exclui a instância do contêiner na saída.
    • A sinalização -e atribui um valor a uma variável de ambiente. -e PORT=$PORT está propagando a variável PORT do sistema local para o contêiner com o mesmo nome de variável.
    • A sinalização -p publica o contêiner como um serviço disponível no localhost na porta 9000. Solicitações para localhost:9000 serão roteadas para o contêiner na porta 8080. Isso significa que a saída do serviço sobre o número da porta em uso não corresponde à forma como o serviço é acessado.
    • O argumento final gcr.io/PROJECT_ID/hello-service é um caminho do repositório que aponta para a versão mais recente da imagem do contêiner. Se não estiver disponível localmente, o docker tentará recuperar a imagem de um registro remoto.

    No navegador, abra http://localhost:9000. Verifique se há mensagens de erro na saída do terminal que correspondam às da observabilidade do Google Cloud.

    Se o problema não for reproduzível localmente, ele poderá ser exclusivo no ambiente de exibição do Knative. Consulte o guia de solução de problemas de veiculação do Knative para áreas específicas a serem investigadas.

    Nesse caso, o erro é reproduzido localmente.

Agora que o erro foi confirmado duplamente como persistente e causado pelo código de serviço em vez da plataforma de hospedagem, é hora de investigar o código mais de perto.

Para os fins deste tutorial, é seguro presumir que o código dentro do contêiner e o código no sistema local sejam idênticos.

Node.js

Encontre a origem da mensagem de erro no arquivo index.js ao redor do número de linha chamado no rastreamento de pilha mostrado nos 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

Encontre a origem da mensagem de erro no arquivo main.py ao redor do número de linha chamado no rastreamento de pilha mostrado nos registros:
NAME = os.getenv("NAME")

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

Go

Encontre a origem da mensagem de erro no arquivo main.go ao redor do número de linha chamado no rastreamento de pilha mostrado nos 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

Encontre a origem da mensagem de erro no arquivo App.java ao redor do número de linha chamado no rastreamento de pilha mostrado nos 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";
}

Ao examinar esse código, as seguintes ações são realizadas quando a variável de ambiente NAME não está definida:

  • Um erro é registrado no Google Cloud Observability
  • Uma resposta de erro HTTP é enviada

O problema é causado por uma variável ausente, mas a causa raiz é mais específica: a alteração de código que adiciona a dependência de uma variável de ambiente não inclui alterações relacionadas aos scripts de implantação e documentação de requisitos de ambiente de execução.

Como corrigir a causa raiz

Agora que coletamos o código e identificamos a possível causa raiz, podemos tomar medidas para corrigi-lo.

  • Verifique se o serviço funciona localmente com o ambiente NAME disponível no local:

    1. Execute o contêiner localmente com a variável de ambiente adicionada:

      PORT=8080 && docker run --rm -e PORT=$PORT -p 9000:$PORT \
       -e NAME="Local World!" \
       gcr.io/PROJECT_ID/hello-service
    2. Navegue para http://localhost:9000.

    3. Consulte "Hello Local World!" que aparece na página.

  • Modifique o ambiente de serviço de veiculação do Knative em execução para incluir esta variável:

    1. Execute o comando de atualização de serviços com o parâmetro --update-env-vars para adicionar uma variável de ambiente:

      gcloud run services update hello-service \
        --update-env-vars NAME=Override
      
    2. Aguarde alguns segundos enquanto a exibição do Knative cria uma nova revisão baseada na revisão anterior com a nova variável de ambiente adicionada.

  • Confirme se o serviço foi corrigido:

    1. Navegue até o URL do serviço do Knative Serving.
    2. Consulte "Hello Override!" que aparece na página.
    3. Verifique se são exibidas mensagens ou erros inesperados no Cloud Logging.

Como melhorar a velocidade das futuras soluções de problemas

Neste exemplo de problema de produção, o erro estava relacionado à configuração operacional. Há alterações de código que minimizam o impacto desse problema no futuro.

  • Melhore o registro de erros para incluir detalhes mais específicos.
  • Em vez de retornar um erro, faça com que o serviço retorne a um padrão seguro. Se o uso de um padrão representar uma alteração na funcionalidade normal, use uma mensagem de aviso para fins de monitoramento.

Vamos remover a variável de ambiente NAME como uma dependência forçada.

  1. Remova o código de manipulação NAME atual:

    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. Adicione o novo código que define um valor substituto:

    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. Teste localmente recriando e executando o contêiner por meio dos casos de configuração afetados:

    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

    Confirme se a variável de ambiente NAME ainda funciona:

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

    Confirme se o serviço funciona sem a variável NAME:

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

    Se o serviço não retornar um resultado, confirme a remoção do código na primeira etapa que não removeu linhas extras, como aquelas usadas para gravar a resposta.

  4. Implemente esse recurso revisitando a seção Implantar seu código.

    Cada implantação em um serviço cria uma nova revisão e inicia automaticamente o tráfego de serviço quando estiver pronto.

    Para limpar as variáveis de ambiente definidas anteriormente:

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

Adicione a nova funcionalidade do valor padrão à cobertura de teste automatizada do serviço.

Como encontrar outros problemas nos registros

Talvez você veja outros problemas no Visualizador de registros deste serviço. Por exemplo, uma chamada de sistema incompatível aparecerá nos registros como uma "Limitação do sandbox de contêiner".

Por exemplo, os serviços do Node.js às vezes resultam nesta mensagem 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.

Nesse caso, a falta de suporte não afeta o serviço de amostra hello-service.

Limpar

Exclua os recursos criados para este tutorial para evitar cobranças.

Como excluir recursos do tutorial

  1. Exclua o serviço de exibição Knative que você implantou neste tutorial:

    gcloud run services delete SERVICE-NAME

    SERVICE-NAME é o nome escolhido do serviço.

    Também é possível excluir os serviços de veiculação do Knative no Console do Google Cloud:

    Acessar a veiculação do Knative

  2. Remova as configurações padrão da gcloud que você adicionou durante a configuração do tutorial:

     gcloud config unset run/platform
     gcloud config unset run/cluster
     gcloud config unset run/cluster_location
    
  3. Remova a configuração do projeto:

     gcloud config unset project
    
  4. Exclua outros recursos do Google Cloud criados neste tutorial:

A seguir