Tutorial: resolução de problemas local de um serviço do Cloud Run


Este tutorial mostra como um programador de serviços pode resolver problemas de um serviço do Cloud Run danificado através das ferramentas de observabilidade do Google Cloud para deteção 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 Cloud Run
  • Use o Relatório de erros e 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

  1. Sign in to your Google Cloud account. If you're new to Google Cloud, create an account to evaluate how our products perform in real-world scenarios. New customers also get $300 in free credits to run, test, and deploy workloads.
  2. In the Google Cloud console, on the project selector page, select or create a Google Cloud project.

    Go to project selector

  3. Verify that billing is enabled for your Google Cloud project.

  4. In the Google Cloud console, on the project selector page, select or create a Google Cloud project.

    Go to project selector

  5. Verify that billing is enabled for your Google Cloud project.

  6. Ative a Cloud Run Admin API
  7. Instale e inicialize a CLI gcloud.
  8. Atualize os componentes:
    gcloud components update
  9. Siga as instruções para instalar o Docker localmente
  10. Funções necessárias

    Para receber as autorizações de que precisa para concluir o tutorial, peça ao seu administrador para lhe conceder as seguintes funções da IAM no seu projeto:

    Para mais informações sobre a atribuição de funções, consulte o artigo Faça a gestão do acesso a projetos, pastas e organizações.

    Também pode conseguir as autorizações necessárias através de funções personalizadas ou outras funções predefinidas.

Configurar predefinições do gcloud

Para configurar o gcloud com as predefinições do seu serviço do Cloud Run:

  1. Defina o projeto predefinido:

    gcloud config set project PROJECT_ID

    Substitua PROJECT_ID pelo nome do projeto que criou para este tutorial.

  2. Configure o gcloud para a região escolhida:

    gcloud config set run/region REGION

    Substitua REGION pela região do Cloud Run suportada à sua escolha.

Localizações do Cloud Run

O Cloud Run é regional, o que significa que a infraestrutura que executa os seus serviços do Cloud Run está localizada numa região específica e é gerida pela Google para estar disponível de forma redundante em todas as zonas dessa região.

O cumprimento dos requisitos de latência, disponibilidade ou durabilidade são fatores principais para selecionar a região onde os seus serviços do Cloud Run são executados. Geralmente, pode selecionar a região mais próxima dos seus utilizadores, mas deve considerar a localização dos outros Google Cloudprodutos usados pelo seu serviço do Cloud Run. A utilização Google Cloud de produtos em conjunto em várias localizações pode afetar a latência do seu serviço, bem como o custo.

O Cloud Run está disponível nas seguintes regiões:

Sujeito aos preços de Nível 1

  • asia-east1 (Taiwan)
  • asia-northeast1 (Tóquio)
  • asia-northeast2 (Osaca)
  • asia-south1 (Mumbai, Índia)
  • europe-north1 (Finlândia) ícone de folha Baixo CO2
  • europe-north2 (Estocolmo) ícone de folha Baixo CO2
  • europe-southwest1 (Madrid) ícone de folha Baixo CO2
  • europe-west1 (Bélgica) ícone de folha Baixo CO2
  • europe-west4 (Países Baixos) ícone de folha Baixo CO2
  • europe-west8 (Milão)
  • europe-west9 (Paris) ícone de folha Baixo CO2
  • me-west1 (Telavive)
  • northamerica-south1 (México)
  • us-central1 (Iowa) ícone de folha Baixo CO2
  • us-east1 (Carolina do Sul)
  • us-east4 (Virgínia do Norte)
  • us-east5 (Columbus)
  • us-south1 (Dallas) ícone de folha Baixo CO2
  • us-west1 (Oregão) ícone de folha Baixo CO2

Sujeito aos preços de Nível 2

  • africa-south1 (Joanesburgo)
  • asia-east2 (Hong Kong)
  • asia-northeast3 (Seul, Coreia do Sul)
  • asia-southeast1 (Singapura)
  • asia-southeast2 (Jacarta)
  • asia-south2 (Deli, Índia)
  • australia-southeast1 (Sydney)
  • australia-southeast2 (Melbourne)
  • europe-central2 (Varsóvia, Polónia)
  • europe-west10 (Berlim) ícone de folha Baixo CO2
  • europe-west12 (Turim)
  • europe-west2 (Londres, Reino Unido) ícone de folha Baixo CO2
  • europe-west3 (Frankfurt, Alemanha)
  • europe-west6 (Zurique, Suíça) ícone de folha Baixo CO2
  • me-central1 (Doha)
  • me-central2 (Dammam)
  • northamerica-northeast1 (Montreal) ícone de folha Baixo CO2
  • northamerica-northeast2 (Toronto) ícone de folha Baixo CO2
  • southamerica-east1 (São Paulo, Brasil) ícone de folha Baixo CO2
  • southamerica-west1 (Santiago, Chile) ícone de folha Baixo CO2
  • us-west2 (Los Angeles)
  • us-west3 (Salt Lake City)
  • us-west4 (Las Vegas)

Se já criou um serviço do Cloud Run, pode ver a região no painel de controlo do Cloud Run na Google Cloud consola.

Montar o código

Crie um novo serviço de saudação do Cloud Run 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 novo diretório do hello-service:

      mkdir hello-service
      cd hello-service
      
    2. Crie um novo projeto do Node.js gerando 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-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"
        }
      }
      

    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
      

    Ir

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

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

      go mod init example.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 novo projeto Maven:

      mvn archetype:generate \
        -DgroupId=com.example.cloudrun \
        -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)

    Ir

    
    // 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
    

    Ir

    
    # 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.23-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 precisar de um Dockerfile nem 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 no Cloud Run.

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

    Em que PROJECT_ID é o ID do projeto do Google Cloud. 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

    Em que PROJECT_ID é o ID do projeto do Google Cloud. 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.

    Ir

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

    Em que PROJECT_ID é o ID do projeto do Google Cloud. 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

    1. Use o gcloud credential helper para autorizar o Docker a enviar para o seu Container Registry.
      gcloud auth configure-docker
    2. Use o plugin do Jib Maven para criar e enviar o contentor para o Container Registry.
      mvn compile jib:build -Dimage=gcr.io/PROJECT_ID/hello-service

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

    Se tiver êxito, 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 projeto do Google Cloud. hello-service é o nome da imagem do contentor e o nome do serviço do Cloud Run. Tenha em atenção que a imagem do contentor é implementada no serviço e na região que configurou anteriormente em Configurar o gcloud

    Responda y, "Sim", ao comando permitir não autenticado. Consulte o artigo Gerir o acesso para mais detalhes sobre a autenticação baseada na IAM.

    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.

É atribuído automaticamente um URL navegável ao serviço.

  1. Navegue para este URL com o seu navegador de Internet:

    1. Abra um navegador de Internet

    2. Encontre o URL do serviço gerado pelo comando de implementação anterior.

      Se o comando de implementação não tiver fornecido um URL, significa que algo correu mal. Reveja a mensagem de erro e aja em conformidade: se não existirem orientações acionáveis, reveja o guia de resolução de problemas e, possivelmente, tente novamente o comando de implementação.

    3. Navegue para este URL copiando-o para a barra de endereço do navegador e premindo ENTER.

  2. Veja o erro HTTP 500 ou HTTP 503.

    Se receber um erro HTTP 403, pode ter rejeitado allow unauthenticated invocations no comando de implementação. Conceda acesso público ao serviço para corrigir este problema:

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

Para mais informações, leia o artigo Permitir acesso público (não autenticado).

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 tratamento desta situação. 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 de observabilidade do Google Cloud disponíveis para recolher mais detalhes:

  1. Use a consola de relatórios de erros, que fornece um painel de controlo com detalhes e acompanhamento da recorrência de erros com um rastreio de pilha reconhecido.

    Aceda à consola de relatórios de erros

    Captura de ecrã da lista de erros, incluindo as colunas &quot;Estado da resolução&quot;, Ocorrências, Erro e &quot;Visto em&quot;.
    Lista de erros registados. Os erros são agrupados por mensagem em revisões, serviços e plataformas.
  2. Clique no erro para ver os detalhes do rastreio da pilha, tendo em atenção as chamadas de funções feitas imediatamente antes do erro.

    Captura de ecrã de um único rastreio de pilha analisado, que demonstra um perfil comum deste erro.
    O "Exemplo de rastreio de pilha" na página de detalhes do erro mostra uma única instância do erro. Pode rever cada instância individual.
  3. Use o Cloud Logging para rever a sequência de operações que originaram o problema, incluindo mensagens de erro que não estão incluídas na consola do Error Reporting devido à falta de um rastreio de pilha de erros reconhecido:

    Aceder à consola do Cloud Logging

    Selecione Revisão do Cloud Run > hello-service na primeira caixa de menu pendente. Esta ação filtra as entradas do registo para as geradas pelo seu serviço.

Leia mais sobre a visualização de registos no Cloud Run

Reverta para uma versão estável

Se for um serviço estabelecido, conhecido por funcionar, existe uma revisão anterior do serviço no Cloud Run. Este tutorial usa um novo serviço sem versões anteriores, pelo que não pode reverter.

No entanto, se tiver um serviço com versões anteriores para as quais pode reverter, siga as instruções em Ver detalhes da revisão para extrair o nome do contentor e os detalhes de configuração necessários para criar uma nova implementação funcional do seu serviço.

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 quaisquer efeitos secundários complicados, a reprodução de erros na produção é segura. No entanto, para muitos serviços reais, 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 no Cloud Run, o que confirma que o problema está ativo quando o serviço está alojado no Cloud Run. 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 Cloud Run:

    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 vai 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 da 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 é uma imagem de contentor tag, uma etiqueta legível para humanos para um identificador de hash sha256 de uma imagem de contentor. Se não estiver disponível localmente, o Docker tenta obter a imagem 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 mensagens em {ops_name}}.

    Se o problema não for reproduzível localmente, pode ser exclusivo do ambiente do Cloud Run. Reveja o guia de resolução de problemas do Cloud Run 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.

Reveja o rastreio da pilha do relatório de erros e faça uma referência cruzada com o código para encontrar as linhas específicas com falhas.

Node.js

Encontre a origem da mensagem de erro no ficheiro index.js junto ao número de 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 de 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.")

Ir

Encontre a origem da mensagem de erro no ficheiro main.go junto ao número de 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 até http://localhost:9000

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

  • Modifique o ambiente do serviço do Cloud Run em execução para incluir esta variável:

    1. Execute o comando de atualização dos serviços para adicionar uma variável de ambiente:

      gcloud run services update hello-service \
        --set-env-vars NAME=Override
      
    2. Aguarde alguns segundos enquanto o Cloud Run 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 do Cloud Run.
    2. Ver a mensagem "Hello Override!" na página.
    3. Verifique se não aparecem mensagens ou erros inesperados no Cloud Logging ou no Error Reporting.

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 regresse 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.")

    Ir

    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))

    Ir

    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 .

    Ir

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

Resolução de problemas do Terraform

Para a resolução de problemas ou questões relacionadas com o Terraform, consulte o artigo Resolução de problemas de validação de políticas do Terraform ou contacte o apoio técnico do Terraform.

Limpar

Se criou um novo projeto para este tutorial, elimine o projeto. Se usou um projeto existente e quer mantê-lo sem as alterações adicionadas neste tutorial, elimine os recursos criados para o tutorial.

Eliminar o projeto

A forma mais fácil de eliminar a faturação é eliminar o projeto que criou para o tutorial.

Para eliminar o projeto:

  1. In the Google Cloud console, go to the Manage resources page.

    Go to Manage resources

  2. In the project list, select the project that you want to delete, and then click Delete.
  3. In the dialog, type the project ID, and then click Shut down to delete the project.

Eliminar recursos do tutorial

  1. Elimine o serviço do Cloud Run 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 do Cloud Run a partir da Google Cloud consola.

  2. Remova a configuração da região predefinida do gcloud que adicionou durante a configuração do tutorial:

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

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

O que se segue?