Ce tutoriel explique comment résoudre un problème de service Knative serving à l'aide d'outils Stackdriver pour la découverte et d'un workflow de développement local pour l'enquête.
Fournie en complément du guide de dépannage, cette étude de cas détaillée utilise un exemple de projet qui génère des erreurs d'exécution lors du déploiement et que vous devez dépanner de manière à trouver et résoudre le problème.
Objectifs
- Écrire, créer et déployer un service sur Knative serving
- Utiliser Cloud Logging pour identifier une erreur
- Récupérer l'image de conteneur à partir de Container Registry pour une analyse de la cause fondamentale
- Corriger le service de production, puis l'améliorer pour atténuer les problèmes futurs
Coûts
Dans ce document, vous utilisez les composants facturables suivants de Google Cloud :
Obtenez une estimation des coûts en fonction de votre utilisation prévue à l'aide du simulateur de coût.
Avant de commencer
- Dans ce tutoriel, nous partons du principe que Knative serving est installé et configuré sur votre cluster.
- Assurez-vous que votre environnement de ligne de commande est configuré et que les outils sont à jour.
- Installez curl pour tester le service.
- Installez Docker localement.
Assembler le code
Créez un service greeter Knative serving étape par étape. Nous vous rappelons que ce service crée une erreur d'exécution intentionnellement pour l'exercice de dépannage.
Créez un projet :
Node.js
Créez un projet Node.js en définissant le package de service, les dépendances initiales et certaines opérations courantes.Créez un répertoire
hello-service
:mkdir hello-service cd hello-service
Générez un fichier
package.json
:npm init --yes npm install --save express@4
Ouvrez le nouveau fichier
package.json
dans votre éditeur et configurez un scriptstart
pour exécuternode index.js
. Lorsque vous avez terminé, le fichier se présente comme suit :{ "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" } }
Si vous continuez de faire évoluer ce service au-delà du présent tutoriel, pensez à renseigner la description et l'auteur, puis à évaluer la licence. Pour en savoir plus, consultez la documentation de package.json.
Python
Créez un répertoire
hello-service
:mkdir hello-service cd hello-service
Créez un fichier requirements.txt et copiez-y vos dépendances :
Go
Créez un répertoire
hello-service
:mkdir hello-service cd hello-service
Créez un projet Go en initialisant un nouveau module Go :
go mod init <var>my-domain</var>.com/hello-service
Vous pouvez mettre à jour le nom si vous le souhaitez : cette opération est conseillée si le code est publié dans un dépôt de code accessible sur le Web.
Java
Créez un projet Maven :
mvn archetype:generate \ -DgroupId=com.example \ -DartifactId=hello-service \ -DarchetypeArtifactId=maven-archetype-quickstart \ -DinteractiveMode=false
Copiez les dépendances dans votre liste de dépendances
pom.xml
(entre les éléments<dependencies>
) :Copiez le paramètre de création dans votre fichier
pom.xml
(sous les éléments<dependencies>
) :
Créez un service HTTP pour gérer les requêtes entrantes :
Node.js
Python
Go
Java
Créez un fichier
Dockerfile
pour définir l'image de conteneur utilisée pour déployer le service :Node.js
Python
Go
Java
Cet exemple utilise Jib pour créer des images Docker à l'aide d'outils Java courants. Jib optimise les builds de conteneurs sans requérir de fichier Dockerfile ni d'installation Docker. Découvrez comment créer des conteneurs Java avec Jib.
Transmettre le code
La transmission du code se déroule en trois étapes : création d'une image de conteneur avec Cloud Build, importation de l'image de conteneur dans Container Registry, puis déploiement de cette image dans Knative serving.
Pour transmettre votre code, procédez comme suit :
Créez votre conteneur et publiez-le dans Container Registry :
Node.js
gcloud builds submit --tag gcr.io/PROJECT_ID/hello-service
où PROJECT_ID correspond à l’ID de votre projet Google Cloud. Vous pouvez vérifier l'ID de votre projet actuel avec
gcloud config get-value project
.En cas de réussite, un message SUCCESS apparaît contenant l'ID, l'heure de création et le nom de l'image. Celle-ci est stockée dans Container Registry et peut être réutilisée au besoin.
Python
gcloud builds submit --tag gcr.io/PROJECT_ID/hello-service
où PROJECT_ID correspond à l’ID de votre projet Google Cloud. Vous pouvez vérifier l'ID de votre projet actuel avec
gcloud config get-value project
.En cas de réussite, un message SUCCESS apparaît contenant l'ID, l'heure de création et le nom de l'image. Celle-ci est stockée dans Container Registry et peut être réutilisée au besoin.
Go
gcloud builds submit --tag gcr.io/PROJECT_ID/hello-service
où PROJECT_ID correspond à l’ID de votre projet Google Cloud. Vous pouvez vérifier l'ID de votre projet actuel avec
gcloud config get-value project
.En cas de réussite, un message SUCCESS apparaît contenant l'ID, l'heure de création et le nom de l'image. Celle-ci est stockée dans Container Registry et peut être réutilisée au besoin.
Java
mvn compile jib:build -Dimage=gcr.io/PROJECT_ID/hello-service
où PROJECT_ID correspond à l’ID de votre projet Google Cloud. Vous pouvez vérifier l'ID de votre projet actuel avec
gcloud config get-value project
.En cas de réussite, un message "BUILD SUCCESS" apparaît. L'image est stockée dans Container Registry et peut être réutilisée si vous le souhaitez.
Exécutez la commande suivante pour déployer votre application :
gcloud run deploy hello-service --image gcr.io/PROJECT_ID/hello-service
Remplacez PROJECT_ID par l'ID de votre projet Google Cloud.
hello-service
correspond à la fois au nom de l'image de conteneur et au nom du service Knative serving. Notez que l'image de conteneur est déployée sur le service et le cluster que vous avez configurés précédemment dans la section Configurer les paramètres par défaut de gcloud.Patientez jusqu'à la fin du déploiement, soit environ 30 secondes. En cas de réussite, la ligne de commande affiche l'URL du service.
Essayez-le !
Essayez le service pour vérifier que vous l'avez bien déployé. Les requêtes doivent échouer avec une erreur HTTP 500 ou 503 (membres de la classe d'erreurs serveur 5xx). Le tutoriel vous explique comment résoudre cette erreur.
Si votre cluster est configuré avec un domaine par défaut routable, ignorez les étapes ci-dessus et copiez l'URL dans votre navigateur Web.
Si vous n'utilisez pas les certificats TLS automatiques ni le mappage de domaine, aucune URL navigable ne vous est fournie pour votre service.
Utilisez plutôt l'URL fournie et l'adresse IP de la passerelle d'entrée du service pour créer une commande curl
pouvant envoyer des requêtes à votre service :
-
Pour obtenir l'adresse IP externe de l'équilibreur de charge, exécutez la commande suivante :
kubectl get svc istio-ingressgateway -n ASM-INGRESS-NAMESPACE
Remplacez ASM-INGRESS-NAMESPACE par l'espace de noms où se trouve votre entrée Cloud Service Mesh. Spécifiez
istio-system
si vous avez installé Cloud Service Mesh à l'aide de sa configuration par défaut.La sortie ressemble à ceci :
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
où la valeur EXTERNAL-IP est votre adresse IP externe de l'équilibreur de charge.
Exécutez une commande
curl
en utilisant cette adresseGATEWAY_IP
dans l'URL.curl -G -H "Host: SERVICE-DOMAIN" https://EXTERNAL-IP/
Remplacez SERVICE-DOMAIN par le domaine par défaut de votre service. Pour ce faire, utilisez l'URL par défaut et supprimez le protocole
http://
.Consultez le message d'erreur HTTP 500 ou HTTP 503.
Examiner le problème
Vérifiez si l'erreur HTTP 5xx rencontrée ci-dessus dans Essayez-le ! s'est produite en tant qu'erreur d'exécution de production. Ce tutoriel vous guide à travers un processus formel de gestion de cette erreur. Bien que les processus de résolution des erreurs de production varient considérablement, ce tutoriel présente une séquence particulière d'étapes pour montrer l'application d'outils et de techniques utiles.
Pour examiner ce problème, procédez comme suit :
- Rassemblez plus de détails sur l'erreur signalée afin d'effectuer une analyse approfondie et de définir une stratégie d'atténuation.
- Pour réduire les répercussions sur l'utilisateur, vous pouvez décider d'appliquer une correction ou un rollback à une version connue et opérationnelle.
- Reproduisez l'erreur pour vérifier que les informations correctes ont bien été collectées et que l'erreur n'est pas ponctuelle.
- Effectuez une analyse des causes fondamentales du bug pour trouver le code, la configuration ou le processus à l'origine de cette erreur.
Au début de l'enquête, vous avez une URL, un horodatage et un message "Erreur de serveur interne".
Collecter des informations supplémentaires
Rassemblez plus d'informations sur le problème pour comprendre ce qui s'est passé et déterminer la marche à suivre.
Utilisez les outils disponibles pour collecter plus de détails :
Affichez les journaux pour obtenir plus de détails.
Utilisez Cloud Logging pour examiner la séquence des opérations menant au problème, y compris les messages d'erreur.
Procéder au rollback vers la version correcte
Si vous avez déjà effectué une révision qui fonctionne, vous pouvez procéder au rollback sur votre service pour utiliser cette révision. Par exemple, vous ne pourrez pas effectuer de rollback sur le nouveau service hello-service
que vous avez déployé dans ce tutoriel, car il ne contient qu'une seule révision.
Pour localiser une révision et effectuer un rollback sur votre service, procédez comme suit :
Reproduire l'erreur
En utilisant les détails que vous avez obtenus précédemment, vérifiez que le problème se produit régulièrement dans les conditions de test.
Envoyez la même requête HTTP en essayant à nouveau le service, et vérifiez si la même erreur et les mêmes détails sont signalés. L'affichage des détails de l'erreur peut prendre un certain temps.
Étant donné que l'exemple de service de ce tutoriel est en lecture seule et ne déclenche aucun effet secondaire compliqué, la reproduction des erreurs en production est sécurisée. Toutefois, pour de nombreux services réels, ce ne sera pas le cas : vous devrez peut-être reproduire les erreurs dans un environnement de test ou limiter cette étape à une enquête locale.
La reproduction de l'erreur permet d'établir le contexte pour la poursuite des travaux. Par exemple, si les développeurs ne peuvent pas reproduire l'erreur, une analyse plus approfondie peut nécessiter une instrumentation supplémentaire du service.
Effectuer une analyse des causes fondamentales
L'analyse des causes fondamentales est une étape importante d'un dépannage efficace pour vous permettre de résoudre le problème plutôt qu'un symptôme.
Auparavant, dans ce tutoriel, vous avez reproduit le problème sur Knative serving, ce qui confirme que le problème se manifeste lorsque le service est hébergé sur Knative serving. À présent, reproduisez le problème localement pour déterminer s'il résulte du code ou s'il apparaît uniquement dans l'hébergement de production.
Si vous n'avez pas utilisé l'interface de ligne de commande Docker en local avec Container Registry, authentifiez-la avec gcloud :
gcloud auth configure-docker
Pour découvrir d'autres approches, consultez la page Méthodes d'authentification de Container Registry.
Si le dernier nom d'image de conteneur utilisé n'est pas disponible, la description du service contient les informations de l'image de conteneur la plus récemment déployée :
gcloud run services describe hello-service
Recherchez le nom de l'image de conteneur dans l'objet
spec
. Une commande plus ciblée peut le récupérer directement :gcloud run services describe hello-service \ --format="value(spec.template.spec.containers.image)"
Cette commande indique un nom d'image de conteneur tel que
gcr.io/PROJECT_ID/hello-service
.Extrayez l'image de conteneur à partir de Container Registry vers votre environnement. Cette opération peut prendre plusieurs minutes lors du téléchargement de l'image de conteneur :
docker pull gcr.io/PROJECT_ID/hello-service
Les mises à jour ultérieures de l'image de conteneur qui réutilisent ce nom peuvent être récupérées avec la même commande. Si vous ignorez cette étape, la commande
docker run
ci-dessous extrait une image de conteneur si celle-ci n'est pas présente sur l'ordinateur local.Exécutez le service localement pour vérifier que le problème n'est pas propre à Knative serving :
PORT=8080 && docker run --rm -e PORT=$PORT -p 9000:$PORT \ gcr.io/PROJECT_ID/hello-service
Voici le détail des éléments de la commande ci-dessus :
- La variable d'environnement
PORT
permet au service de déterminer le port d'écoute au sein du conteneur. - La commande
run
démarre le conteneur, affichant par défaut la commande "entrypoint" définie dans le fichier Dockerfile ou une image de conteneur parent. - L'option
--rm
supprime l'instance de conteneur en cas de fermeture. - L'option
-e
attribue une valeur à une variable d'environnement.-e PORT=$PORT
propage la variablePORT
du système local dans le conteneur avec le même nom de variable. - L'option
-p
publie le conteneur en tant que service disponible sur localhost sur le port 9000. Les requêtes vers localhost:9000 seront acheminées vers le conteneur sur le port 8080. Cela signifie que le numéro de port de sortie utilisé ne correspondra pas au numéro de port permettant d'accéder au service. - L'argument final
gcr.io/PROJECT_ID/hello-service
est un chemin d'accès au dépôt pointant vers la dernière version de l'image de conteneur. Si elle n'est pas disponible localement, Docker tente de récupérer l'image à partir d'un registre distant.
Dans votre navigateur, ouvrez http://localhost:9000. Vérifiez la sortie du terminal en cas de messages d'erreur qui correspondent à ceux de Google Cloud Observability.
Si le problème ne peut être reproduit localement, il peut être unique à l'environnement Knative serving. Consultez le guide de dépannage de Knative serving pour connaître les domaines spécifiques à examiner.
Dans ce cas, l'erreur est reproduite localement.
- La variable d'environnement
Maintenant que l'erreur est confirmée par deux fois comme étant persistante et causée par le code de service plutôt que par la plate-forme d'hébergement, il est temps d'examiner le code de plus près.
Pour les besoins de ce tutoriel, vous pouvez supposer que le code du conteneur et le code du système local sont identiques.
Node.js
Recherchez la source du message d'erreur dans le fichierindex.js
autour du numéro de ligne indiqué dans la trace de pile affichée dans les journaux :
Python
Recherchez la source du message d'erreur dans le fichiermain.py
autour du numéro de ligne indiqué dans la trace de pile affichée dans les journaux :
Go
Recherchez la source du message d'erreur dans le fichier main.go
autour du numéro de ligne indiqué dans la trace de pile affichée dans les journaux :
Java
Recherchez la source du message d'erreur dans le fichier App.java
autour du numéro de ligne indiqué dans la trace de pile affichée dans les journaux :
En examinant le code, on remarque que les actions suivantes sont effectuées alors que la variable d'environnement NAME
n'est pas définie :
- Une erreur est consignée dans Google Cloud Observability.
- Une réponse d'erreur HTTP est envoyée.
Le problème est causé par une variable manquante, mais la cause fondamentale est plus spécifique : la modification du code qui a créé une dépendance forte envers une variable d'environnement n'incluait pas les modifications liées aux scripts de déploiement et à la documentation concernant les exigences d'exécution.
Corriger la cause fondamentale
Maintenant que nous avons collecté le code et identifié la cause fondamentale potentielle, nous pouvons prendre des mesures pour la corriger.
Vérifiez que le service fonctionne localement avec l'environnement
NAME
disponible sur place :Exécutez le conteneur localement avec la variable d'environnement ajoutée :
PORT=8080 && docker run --rm -e PORT=$PORT -p 9000:$PORT \ -e NAME="Local World!" \ gcr.io/PROJECT_ID/hello-service
Accédez à http://localhost:9000 dans votre navigateur.
"Hello Local World !" apparaît sur la page.
Modifiez l'environnement de service Knative serving en cours d'exécution pour inclure cette variable :
Exécutez la commande de mise à jour des services avec le paramètre
--update-env-vars
pour ajouter une variable d'environnement :gcloud run services update hello-service \ --update-env-vars NAME=Override
Patientez quelques secondes pendant que Knative serving crée une révision basée sur la révision précédente avec la nouvelle variable d'environnement ajoutée.
Vérifiez que le problème de service est maintenant corrigé :
- Accédez à l'URL du service Knative serving dans votre navigateur.
- "Hello Override !" apparaît sur la page.
- Vérifiez qu'aucune erreur ni aucun message inattendu n'apparaît dans Cloud Logging.
Accélérer les prochains dépannages
Dans cet exemple de problème de production, l'erreur était liée à la configuration opérationnelle. Des modifications sont apportées au code afin de minimiser l'impact de ce problème à l'avenir.
- Améliorez le journal d'erreurs pour inclure des détails plus spécifiques.
- Au lieu de renvoyer une erreur, demandez au service de revenir à une valeur par défaut sûre. Si l'utilisation d'une valeur par défaut implique une modification de la fonctionnalité normale, utilisez un message d'avertissement à des fins de surveillance.
Passons maintenant à la suppression d'une dépendance forte envers la variable d'environnement NAME
.
Supprimez le code de gestion
NAME
existant :Node.js
Python
Go
Java
Ajoutez du code définissant une valeur de remplacement :
Node.js
Python
Go
Java
Effectuez un test local en recréant et en exécutant le conteneur dans les cas de configuration concernés :
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
Vérifiez que la variable d'environnement
NAME
fonctionne toujours :PORT=8080 && docker run --rm -e $PORT -p 9000:$PORT \ -e NAME="Robust World" \ gcr.io/PROJECT_ID/hello-service
Vérifiez que le service fonctionne sans la variable
NAME
:PORT=8080 && docker run --rm -e $PORT -p 9000:$PORT \ gcr.io/PROJECT_ID/hello-service
Si le service ne renvoie pas de résultat, vérifiez que vous n'avez pas supprimé de lignes supplémentaires lors de la suppression de code de la première étape, comme par exemple les lignes utilisées pour rédiger la réponse.
Déployez le code en consultant à nouveau la section Déployer votre code.
À chaque déploiement sur le service, une révision est créée et le trafic commence automatiquement à être diffusé une fois le déploiement prêt.
Pour effacer les variables d'environnement définies précédemment, exécutez la commande suivante :
gcloud run services update hello-service --clear-env-vars
Ajoutez la nouvelle fonctionnalité de valeur par défaut au champ d'application des tests automatisés pour le service.
Rechercher d'autres problèmes dans les journaux
D'autres problèmes peuvent survenir dans la visionneuse de journaux de ce service. Par exemple, un appel système non compatible apparaîtra dans les journaux comme une "limitation du bac à sable de conteneur".
Par exemple, les services Node.js génèrent parfois ce message de journal :
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.
Dans ce cas, la non-compatibilité n'a aucune incidence sur l'exemple de service "hello-service".
Effectuer un nettoyage
Vous pouvez supprimer les ressources créées pour ce tutoriel afin d'éviter que des frais ne vous soient facturés.
Supprimer les ressources du tutoriel
Supprimez le service Knative serving que vous avez déployé dans ce tutoriel :
gcloud run services delete SERVICE-NAME
Où SERVICE-NAME est le nom de service que vous avez choisi.
Vous pouvez également supprimer les services Knative serving à partir de la console Google Cloud.
Supprimez les configurations gcloud par défaut que vous avez ajoutées lors de la configuration du tutoriel :
gcloud config unset run/platform gcloud config unset run/cluster gcloud config unset run/cluster_location
Supprimez la configuration du projet :
gcloud config unset project
Supprimez les autres ressources Google Cloud créées dans ce tutoriel :
- Supprimez l'image de conteneur nommée
gcr.io/<var>PROJECT_ID</var>/hello-service
de Container Registry. - Si vous avez créé un cluster pour ce tutoriel, supprimez-le.
- Supprimez l'image de conteneur nommée
Étape suivante
- Découvrez comment obtenir des renseignements sur le comportement en production à l'aide de Cloud Logging.
- Pour en savoir plus, consultez la page Dépannage de Knative serving,
- Découvrez des architectures de référence, des schémas et des bonnes pratiques concernant Google Cloud. Consultez notre Cloud Architecture Center.