Resolução de problemas local


Este tutorial mostra como resolver problemas de um serviço de publicação do Knative danificado através das ferramentas do Stackdriver para descoberta e um fluxo de trabalho de desenvolvimento local para investigação.

Este "estudo de caso" passo a passo complementar ao guia de resolução de problemas usa um projeto de exemplo que resulta em erros de tempo de execução quando implementado, que resolve para encontrar e corrigir o problema.

Objetivos

  • Escreva, crie e implemente um serviço no Knative Serving
  • Use o Cloud Logging para identificar um erro
  • Obtenha a imagem do contentor do Container Registry para uma análise da causa principal
  • Corrija o serviço de "produção" e, em seguida, melhore o serviço para mitigar problemas futuros

Custos

Neste documento, usa os seguintes componentes faturáveis do Google Cloud:

Para gerar uma estimativa de custos com base na sua utilização projetada, use a calculadora de preços.

Os novos Google Cloud utilizadores podem ser elegíveis para uma avaliação gratuita.

Antes de começar

Montar o código

Crie um novo serviço de saudação do Knative Serving passo a passo. Lembre-se de que este serviço cria intencionalmente um erro de tempo de execução para o exercício de resoluçã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. Crie um diretório hello-service:

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

      npm init --yes
      npm install express@4
      
    3. Abra o novo ficheiro package.json no editor e configure um start script para executar node index.js. Quando terminar, o ficheiro tem o seguinte aspeto:

      {
        "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 continuar a desenvolver este serviço para além do tutorial imediato, considere preencher a descrição, o autor e avaliar a licença. Para mais detalhes, leia a documentação package.json.

    Python

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

      mkdir hello-service
      cd hello-service
      
    2. Crie um ficheiro requirements.txt e copie as suas dependências para o mesmo:

      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. Crie um diretório hello-service:

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

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

    Pode atualizar o nome específico como quiser: deve atualizar o nome se o código for publicado num repositório de código acessível através da Web.

    Java

    1. Crie um projeto Maven:

      mvn archetype:generate \
        -DgroupId=com.example \
        -DartifactId=hello-service \
        -DarchetypeArtifactId=maven-archetype-quickstart \
        -DinteractiveMode=false
      
    2. Copie as dependências para a 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 definição de compilação para o seu 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 processar pedidos recebidos:

    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 contentor usada para implementar o serviço:

    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

    Este exemplo usa o Jib para criar imagens do Docker com ferramentas Java comuns. O Jib otimiza as compilações de contentores sem necessidade de um Dockerfile ou de ter o Docker instalado. Saiba mais sobre como criar contentores 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>
    

Envio do código

O código de envio consiste em três passos: criar uma imagem de contentor com o Cloud Build, carregar a imagem de contentor para o Container Registry e implementar a imagem de contentor para o Knative Serving.

Para enviar o código:

  1. Crie o contentor e publique-o no Container Registry:

    Node.js

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

    Onde PROJECT_ID é o ID do seu Google Cloud projeto. Pode verificar o ID do projeto atual com gcloud config get-value project.

    Após o êxito, deve ver uma mensagem SUCCESS com o ID, a hora de criação e o nome da imagem. A imagem é armazenada no Container Registry e pode ser reutilizada, se quiser.

    Python

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

    Onde PROJECT_ID é o ID do seu Google Cloud projeto. Pode verificar o ID do projeto atual com gcloud config get-value project.

    Após o êxito, deve ver uma mensagem SUCCESS com o ID, a hora de criação e o nome da imagem. A imagem é armazenada no Container Registry e pode ser reutilizada, se quiser.

    Go

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

    Onde PROJECT_ID é o ID do seu Google Cloud projeto. Pode verificar o ID do projeto atual com gcloud config get-value project.

    Após o êxito, deve ver uma mensagem SUCCESS com o ID, a hora de criação e o nome da imagem. A imagem é armazenada no Container Registry e pode ser reutilizada, se quiser.

    Java

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

    Onde PROJECT_ID é o ID do seu Google Cloud projeto. Pode verificar o ID do projeto atual com gcloud config get-value project.

    Se for bem-sucedido, deve ver a mensagem BUILD SUCCESS. A imagem é armazenada no Container Registry e pode ser reutilizada, se quiser.

  2. Execute o seguinte comando para implementar a sua app:

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

    Substitua PROJECT_ID pelo ID do seu Google Cloud projeto. hello-service é o nome da imagem do contentor e o nome do serviço Knative Serving. Tenha em atenção que a imagem do contentor é implementada no serviço e no cluster que configurou anteriormente em Configurar o gcloud

    Aguarde até que a implementação esteja concluída. Este processo pode demorar cerca de meio minuto. Se for bem-sucedido, a linha de comandos apresenta o URL do serviço.

Experimentar

Experimente o serviço para confirmar que o implementou com êxito. Os pedidos devem falhar com um erro HTTP 500 ou 503 (membros da classe 5xx Server errors). O tutorial explica como resolver este erro de resposta.

Se o seu cluster estiver configurado com um domínio predefinido encaminhável, ignore os passos acima e, em alternativa, copie o URL para o seu navegador de Internet.

Se não usar certificados TLS automáticos e mapeamento de domínios, não lhe é fornecido um URL navegável para o seu serviço.

Em alternativa, use o URL fornecido e o endereço IP do gateway de entrada do serviço para criar um comando curl que possa fazer pedidos ao seu serviço:

  1. Para obter 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 espaço de nomes onde se encontra a entrada do Cloud Service Mesh. Especifique istio-system se tiver instalado o Cloud Service Mesh com a respetiva configuração predefinida.

    O resultado tem um aspeto semelhante ao seguinte:

    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 seu endereço IP externo do LoadBalancer.

  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 atribuído predefinido do seu serviço. Pode obtê-lo acedendo ao URL predefinido e removendo o protocolo http://.

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

Investigar o problema

Visualize que o erro HTTP 5xx encontrado acima em Experimentar foi encontrado como um erro de tempo de execução de produção. Este tutorial explica um processo formal para o fazer. Embora os processos de resolução de erros de produção variem muito, este tutorial apresenta uma sequência específica de passos para mostrar a aplicação de ferramentas e técnicas úteis.

Para investigar este problema, vai trabalhar nestas fases:

  • Recolha mais detalhes sobre o erro comunicado para apoiar uma investigação mais aprofundada e definir uma estratégia de mitigação.
  • Alivie o impacto no utilizador decidindo avançar com uma correção ou reverter para uma versão conhecida e em bom estado.
  • Reproduza o erro para confirmar que foram recolhidos os detalhes corretos e que o erro não é uma falha única
  • Faça uma análise da causa principal do erro para encontrar o código, a configuração ou o processo que criou este erro

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

A recolher mais detalhes

Recolher mais informações sobre o problema para compreender o que aconteceu e determinar os passos seguintes.

Use as ferramentas disponíveis para recolher mais detalhes:

  1. Veja os registos para mais detalhes.

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

Reverta para uma versão estável

Se tiver uma revisão que sabe que estava a funcionar, pode reverter o seu serviço para usar essa revisão. Por exemplo, não pode reverter o serviço hello-service que implementou neste tutorial porque contém apenas uma revisão.

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

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

  2. Migrar todo o tráfego para a revisão em bom estado.

Reproduzir o erro

Com os detalhes que obteve anteriormente, confirme se o problema ocorre consistentemente em condições de teste.

Envie o mesmo pedido HTTP experimentando-o novamente e veja se o mesmo erro e detalhes são comunicados. Os detalhes dos erros podem demorar algum tempo a aparecer.

Uma vez que o serviço de exemplo neste tutorial é só de leitura e não aciona efeitos secundários complicados, a reprodução de erros na produção é segura. No entanto, para muitos serviços reais, este não é o caso: pode ter de reproduzir erros num ambiente de teste ou limitar este passo à investigação local.

A reprodução do erro estabelece o contexto para trabalho adicional. Por exemplo, se os programadores não conseguirem reproduzir o erro, a investigação adicional pode exigir uma instrumentação adicional do serviço.

Realizar uma análise da causa principal

A análise da causa principal é um passo importante na resolução de problemas eficaz para garantir que corrige o problema em vez de um sintoma.

Anteriormente, neste tutorial, reproduziu o problema na publicação do Knative, o que confirma que o problema está ativo quando o serviço está alojado na publicação do Knative. Agora, reproduza o problema localmente para determinar se o problema está isolado no código ou se só surge na produção de alojamento.

  1. Se não tiver usado a CLI Docker localmente com o Container Registry, autentique-a com o gcloud:

    gcloud auth configure-docker

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

  2. Se o nome da imagem do contentor usado mais recentemente não estiver disponível, a descrição do serviço tem as informações da imagem do contentor implementada mais recentemente:

    gcloud run services describe hello-service

    Encontre o nome da imagem do contentor no objeto spec. Um comando mais segmentado pode obtê-lo diretamente:

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

    Este comando revela um nome de imagem de contentor, como gcr.io/PROJECT_ID/hello-service.

  3. Extraia a imagem do contentor do Container Registry para o seu ambiente. Este passo pode demorar vários minutos, uma vez que transfere a imagem do contentor:

    docker pull gcr.io/PROJECT_ID/hello-service

    As atualizações posteriores à imagem do contentor que reutilizam este nome podem ser obtidas com o mesmo comando. Se ignorar este passo, o comando docker run abaixo extrai uma imagem de contentor se não existir nenhuma na máquina local.

  4. Execute localmente para confirmar que o problema não é exclusivo do Knative Serving:

    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 na qual o serviço deve escutar dentro do contentor.
    • O comando run inicia o contentor, usando por predefinição o comando de ponto de entrada definido no Dockerfile ou numa imagem de contentor principal.
    • A flag --rm elimina a instância do contentor ao sair.
    • A flag -e atribui um valor a uma variável de ambiente. -e PORT=$PORT está a propagar a variável PORT do sistema local para o contentor com o mesmo nome de variável.
    • A flag -p publica o contentor como um serviço disponível em localhost na porta 9000. Os pedidos para localhost:9000 são encaminhados para o contentor na porta 8080. Isto significa que a saída do serviço sobre o número da porta em utilização não vai corresponder à forma como o serviço é acedido.
    • 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 contentor. Se não estiver disponível localmente, o Docker tenta obter a imagem a partir de um registo remoto.

    No navegador, abra http://localhost:9000. Verifique a saída do terminal para ver se existem mensagens de erro que correspondam às do Google Cloud Observability.

    Se o problema não for reproduzível localmente, pode ser exclusivo do ambiente de publicação do Knative. Reveja o guia de resolução de problemas de publicação do Knative para ver áreas específicas a investigar.

    Neste caso, o erro é reproduzido localmente.

Agora que o erro está duplamente confirmado como persistente e causado pelo código do serviço em vez da plataforma de alojamento, é altura de investigar o código mais detalhadamente.

Para efeitos deste tutorial, é seguro assumir que o código no contentor e o código no sistema local são idênticos.

Node.js

Encontre a origem da mensagem de erro no ficheiro index.js junto ao número da linha indicado no rastreio da pilha apresentado nos registos:
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 ficheiro main.py junto ao número da linha indicado no rastreio da pilha apresentado nos registos:
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 ficheiro main.go junto ao número da linha indicado no rastreio da pilha apresentado nos registos:

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 ficheiro App.java junto ao número da linha indicado no rastreio da pilha apresentado nos registos:

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 este código, são tomadas as seguintes ações quando a variável de ambiente NAME não está definida:

  • É registado um erro no Google Cloud Observability
  • É enviada uma resposta de erro HTTP

O problema é causado por uma variável em falta, mas a causa principal é mais específica: a alteração do código que adiciona a dependência rígida numa variável de ambiente não incluiu alterações relacionadas aos scripts de implementação e à documentação dos requisitos de tempo de execução.

Corrigir a causa principal

Agora que recolhemos o código e identificámos a potencial causa principal, podemos tomar medidas para a corrigir.

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

    1. Execute o contentor 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 no seu navegador para http://localhost:9000

    3. Ver a mensagem "Olá, mundo local!" na página

  • Modifique o ambiente de serviç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 o Knative serving cria uma nova revisão com base na revisão anterior com a nova variável de ambiente adicionada.

  • Confirme que o serviço está agora corrigido:

    1. Navegue no navegador para o URL do serviço Knative serving.
    2. Ver a mensagem "Hello Override!" na página.
    3. Verifique se não aparecem mensagens ou erros inesperados no Cloud Logging.

Melhorar a velocidade da resolução de problemas futuros

Neste exemplo de problema de produção, o erro estava relacionado com a configuração operacional. Existem alterações ao código que vão minimizar o impacto deste problema no futuro.

  • Melhorar o registo de erros para incluir detalhes mais específicos.
  • Em vez de devolver um erro, faça com que o serviço recorra a uma predefinição segura. Se a utilização de um valor predefinido representar uma alteração à funcionalidade normal, utilize uma mensagem de aviso para fins de monitorização.

Vamos ver como remover a variável de ambiente NAME como uma dependência rígida.

  1. Remova o código de processamento de NAME existente:

    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 um novo código que defina um 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. Teste localmente recompilando e executando o contentor através 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 que 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 devolver um resultado, confirme que a remoção do código no primeiro passo não removeu linhas adicionais, como as usadas para escrever a resposta.

  4. Implemente-o voltando à secção Implemente o seu código.

    Cada implementação num serviço cria uma nova revisão e começa automaticamente a publicar tráfego quando estiver pronta.

    Para limpar as variáveis de ambiente definidas anteriormente:

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

Adicione a nova funcionalidade para o valor predefinido à cobertura de testes automatizados para o serviço.

Encontrar outros problemas nos registos

Pode ver outros problemas no visualizador de registos deste serviço. Por exemplo, uma chamada de sistema não suportada aparece nos registos como uma "Limitação da sandbox do contentor".

Por exemplo, os serviços Node.js resultam, por vezes, nesta mensagem de registo:

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.

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

Limpar

Pode eliminar os recursos criados para este tutorial para evitar incorrer em custos.

Eliminar recursos do tutorial

  1. Elimine o serviço Knative Serving que implementou neste tutorial:

    gcloud run services delete SERVICE-NAME

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

    Também pode eliminar serviços Knative serving a partir da Google Cloud consola:

    Aceda ao Knative serving

  2. Remova as configurações predefinidas do gcloud que 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. Elimine outros Google Cloud recursos criados neste tutorial:

O que se segue?