Neste tutorial, mostramos como resolver problemas de um serviço de fornecimento do Knative corrompido usando as 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 no 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.
Antes de começar
- Neste tutorial, presumimos que você tenha o Knative serving instalado e configurado no cluster.
- Verifique se o ambiente da linha de comando está configurado e se as ferramentas estão atualizadas.
- Instale o curl para testar o serviço.
- Instale o Docker localmente.
Como montar o código
Crie um novo serviço de chatbot do Knative serving, passo a passo. 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.
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.Criar um diretório
hello-service
:mkdir hello-service cd hello-service
Gere um arquivo
package.json
:npm init --yes npm install --save express@4
Abra o novo arquivo
package.json
no seu editor e configure um scriptstart
para executarnode 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
Crie um novo diretório
hello-service
:mkdir hello-service cd hello-service
Crie um arquivo requirements.txt e copie suas dependências para ele:
Go
Criar um diretório
hello-service
:mkdir hello-service cd hello-service
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
Criar um projeto Maven:
mvn archetype:generate \ -DgroupId=com.example \ -DartifactId=hello-service \ -DarchetypeArtifactId=maven-archetype-quickstart \ -DinteractiveMode=false
Copie as dependências para sua lista de dependências
pom.xml
(entre os elementos<dependencies>
):Copie a configuração de build para o
pom.xml
(nos elementos<dependencies>
):
Crie um serviço HTTP para lidar com solicitações de entrada:
Node.js
Python
Go
Java
Crie um
Dockerfile
para definir a imagem do contêiner usada para implantar o serviço:Node.js
Python
Go
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.
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 de contêiner no Container Registry e implantar a imagem de contêiner no Knative serving.
Para enviar o código, siga estas etapas:
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.
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:
-
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 Cloud Service Mesh está localizada. Especifique
istio-system
se você instalou o Cloud 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.
Execute um comando
curl
usando este endereçoGATEWAY_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://
.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:
Veja os registros para mais detalhes.
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:
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 no Knative serving Isso confirma que o problema está ativo quando o serviço é hospedado o Knative serving. Agora, reproduza o problema localmente para determinar se ele está isolado do código ou se ele só aparece na hospedagem de produção.
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.
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
.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.Execute localmente para confirmar que o problema não é exclusivo do serviço 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ávelPORT
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 correspondem às da Google Cloud Observability.
Se o problema não for reproduzível localmente, ele poderá ser exclusivo do ambiente de veiculação do Knative. Analise o Guia de solução de problemas do Knative serving áreas específicas para investigar.
Nesse caso, o erro é reproduzido localmente.
- A variável de ambiente
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 arquivoindex.js
ao redor do número de linha chamado no rastreamento de pilha mostrado nos registros:
Python
Encontre a origem da mensagem de erro no arquivomain.py
ao redor do número de linha chamado no rastreamento de pilha mostrado nos registros:
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:
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:
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: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
Navegue para http://localhost:9000.
Consulte "Hello Local World!" que aparece na página.
Modifique o ambiente de serviço do Knative serving em execução para incluir este :
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
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 se o serviço foi corrigido:
- Acesse o URL do serviço do Knative serving no navegador.
- Consulte "Hello Override!" que aparece na página.
- 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.
Remova o código de manipulação
NAME
atual:Node.js
Python
Go
Java
Adicione o novo código que define um valor substituto:
Node.js
Python
Go
Java
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.
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
Exclua o serviço do Knative serving 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 do Knative serving no Console do Google Cloud.
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
Remova a configuração do projeto:
gcloud config unset project
Exclua outros recursos do Google Cloud criados neste tutorial:
- Exclua a imagem de contêiner denominada
gcr.io/<var>PROJECT_ID</var>/hello-service
do Container Registry. - Se você criou um cluster para este tutorial, exclua-o.
- Exclua a imagem de contêiner denominada
A seguir
- Saiba mais sobre como usar o Cloud Logging para ter insights sobre o comportamento de produção.
- Saiba mais sobre a solução de problemas do Knative serving.
- Confira arquiteturas de referência, diagramas, tutoriais e práticas recomendadas do Google Cloud. Confira o Centro de arquitetura do Cloud.