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.
Antes de começar
- Este tutorial pressupõe que tem o Knative serving instalado e configurado no seu cluster.
- Certifique-se de que o ambiente de linha de comandos está configurado e que as ferramentas estão atualizadas:
- Instale o curl para experimentar o serviço.
- Instale o Docker localmente.
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.
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.Crie um diretório
hello-service
:mkdir hello-service cd hello-service
Gere um ficheiro
package.json
:npm init --yes npm install express@4
Abra o novo ficheiro
package.json
no editor e configure umstart
script para executarnode 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
Crie um novo diretório do
hello-service
:mkdir hello-service cd hello-service
Crie um ficheiro requirements.txt e copie as suas dependências para o mesmo:
Go
Crie um diretório
hello-service
:mkdir hello-service cd hello-service
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
Crie um projeto Maven:
mvn archetype:generate \ -DgroupId=com.example \ -DartifactId=hello-service \ -DarchetypeArtifactId=maven-archetype-quickstart \ -DinteractiveMode=false
Copie as dependências para a sua lista de dependências
pom.xml
(entre os elementos<dependencies>
):Copie a definição de compilação para o seu
pom.xml
(nos elementos<dependencies>
):
Crie um serviço HTTP para processar pedidos recebidos:
Node.js
Python
Go
Java
Crie um
Dockerfile
para definir a imagem do contentor usada para implementar o serviço:Node.js
Python
Go
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.
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:
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.
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 gcloudAguarde 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:
-
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.
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 atribuído predefinido do seu serviço. Pode obtê-lo acedendo ao URL predefinido e removendo o protocolo
http://
.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:
Veja os registos para mais detalhes.
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:
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.
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.
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
.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.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ávelPORT
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.
- A variável de ambiente
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 ficheiroindex.js
junto ao número da linha indicado no rastreio da pilha apresentado nos registos:
Python
Encontre a origem da mensagem de erro no ficheiromain.py
junto ao número da linha indicado no rastreio da pilha apresentado nos registos:
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:
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:
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
NAME
ambiente disponível no local: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
Navegue no seu navegador para http://localhost:9000
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:
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 que o serviço está agora corrigido:
- Navegue no navegador para o URL do serviço Knative serving.
- Ver a mensagem "Hello Override!" na página.
- 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.
Remova o código de processamento de
NAME
existente:Node.js
Python
Go
Java
Adicione um novo código que defina um valor alternativo:
Node.js
Python
Go
Java
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.
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
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:
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
Remova a configuração do projeto:
gcloud config unset project
Elimine outros Google Cloud recursos criados neste tutorial:
- Elimine a imagem do contentor denominada
gcr.io/<var>PROJECT_ID</var>/hello-service
do Container Registry. - Se criou um cluster para este tutorial, elimine o cluster.
- Elimine a imagem do contentor denominada
O que se segue?
- Saiba como usar o Cloud Logging para obter estatísticas sobre o comportamento de produção.
- Para mais informações sobre a resolução de problemas de publicação do Knative.
- Explore arquiteturas de referência, diagramas e práticas recomendadas sobre o Google Cloud. Consulte o nosso Centro de arquitetura na nuvem.