Vérifier et supprimer
- Leçon 15
- 50 min
- Module 5 : Déployer et nettoyer
Leçon 15 sur 15 · L'essentiel de Docker et du nuage pour les développeurs en IAVérifier et supprimer
1. Comprendre les conteneurs
2. Construire l'image de l'application
3. Réseau, configuration et persistance
4. Les services de base du nuage
5. Déployer et nettoyer
Ce que vous saurez faire
« Succeeded » (réussi) n’est pas une preuve. Envoyez les requêtes qui montrent que le déploiement fonctionne et échoue sans danger, puis lisez ses journaux. Supprimez ensuite tout, avec une liste qui prouve que vous n’avez rien oublié.
Avant de commencerCe qu’il faut savoir et avoir sous la main
Ce que vous devez savoir
- Un test de bon fonctionnement, vu dans Vérifier et nettoyer : une vérification rapide du cours Créer et déployer votre première API.
- L’identifiant de requête dans chaque ligne de journal, vu dans Configuration et journalisation : un identifiant pour chaque requête du cours sur les API.
- Le passage à zéro instance, vu dans Connaître les coûts : passage à zéro et requêtes que vous n’avez pas envoyées.
- L’inventaire et la colonne « peut-elle entraîner des frais? », vus dans Connaître les coûts : un inventaire des ressources.
- Les révisions et les journaux système, vus dans Configuration opérationnelle : exemple détaillé : appliquer le fichier (parcours réel).
Ce qu’il vous faut sur votre ordinateur
- Parcours en ligne : l’application conteneurisée avec les sondes de
deploy/containerapp.yaml, vue dans Configuration opérationnelle : exemple détaillé : appliquer le fichier (parcours réel). Il vous faut son adresse et sa clé, obtenues dans Registre et déploiement : étape 6 : créer l’application conteneurisée. - Parcours simulé : la simulation locale sur
127.0.0.1:8080, vue dans Registre et déploiement : le parcours simulé : les mêmes étapes sur votre ordinateur. Le registre local vient de Registre, dépôt et étiquette. La clé se trouve danssecrets/api_key.txt. docs/cloud-design.mdavec votre inventaire, vu dans Connaître les coûts : Exercice du module. Cette leçon créeDEPLOY.mdà côté.- Si le déploiement en ligne a disparu ou ne fonctionne plus, recréez-le avec Registre et déploiement : l’option réelle : créer le déploiement. Pour les fichiers du projet, utilisez l’instantané
end-of-m05, comme dans Utiliser un instantané.
Testé avec Docker Desktop 4.81.0 (Docker Engine 29.6.1) sur macOS, avec une puce Apple. Les étapes Azure ont été exécutées avec Azure CLI 2.88.0 et l’extension containerapp 1.3.0b5, dans la région canadacentral. Les étapes Windows et Linux ne sont pas testées.
Tests de bon fonctionnement : ce que ce déploiement doit prouver
Section intitulée « Tests de bon fonctionnement : ce que ce déploiement doit prouver »Un test de bon fonctionnement est une courte série de requêtes envoyées juste après un déploiement. pytest a déjà testé le code. Le test de bon fonctionnement vérifie ce déploiement : cette image, ces paramètres, ce secret, cette adresse. Chaque vérification prouve une chose :
| Requête | Résultat attendu | Ce qu’elle prouve |
|---|---|---|
GET /health |
200 {"status":"ok"} |
Le processus fonctionne et le point d’entrée l’atteint. |
GET /ready |
200 {"status":"ready"} |
Le classificateur peut fonctionner. |
POST /v1/classify avec la clé |
200 et une catégorie |
Le secret est parvenu à l’application. |
POST /v1/classify sans la clé |
401 |
L’API n’est pas ouverte à tous. |
GET /docs |
404 |
SHOW_DOCS=false est parvenu à l’application. |
http://…/health (en ligne seulement) |
301 vers https:// |
Aucun HTTP non chiffré. |
GET /v1/history avec la clé |
503 history_unavailable (historique indisponible) |
Résultat attendu : ce déploiement n’a pas de base de données. |
Deux vérifications attendent un échec. Un déploiement qui répond 200 sans clé est défectueux, même si toutes les autres vérifications réussissent.
Exemple détaillé : les vérifications en ligne
Section intitulée « Exemple détaillé : les vérifications en ligne »Amira définit l’adresse une seule fois. La première requête arrive après une période d’inactivité, alors elle mesure sa durée :
URL=https://ticket-api.nicewater-a307f95a.canadacentral.azurecontainerapps.iotime curl -s $URL/health$URL = "https://ticket-api.nicewater-a307f95a.canadacentral.azurecontainerapps.io"Measure-Command { curl.exe -s "$URL/health" }La sortie (en ligne, abrégée) :
{"status":"ok"}real 18.4s18 secondes pour une petite requête. L’application n’avait aucune réplique. La plateforme devait donc d’abord en démarrer une : c’est un démarrage à froid. Les requêtes suivantes ont pris moins d’une seconde. Réveillez l’application avec /health avant une vérification chronométrée. Ainsi, un démarrage à froid ne donnera pas l’impression d’une API lente.
Ensuite, les autres vérifications. $KEY est la clé de Registre et déploiement : étape 6 : créer l’application conteneurisée, encore dans le terminal :
curl -s $URL/readycurl -s -X POST $URL/v1/classify -H 'Content-Type: application/json' -H "X-API-Key: $KEY" -d '{"subject":"Charged twice for March"}'curl -s -o /dev/null -w "%{http_code}\n" -X POST $URL/v1/classify -H 'Content-Type: application/json' -d '{"subject":"x"}'curl -s -o /dev/null -w "%{http_code}\n" $URL/docscurl -s $URL/v1/history -H "X-API-Key: $KEY"curl.exe -s "$URL/ready"curl.exe -s -X POST "$URL/v1/classify" -H "Content-Type: application/json" -H "X-API-Key: $KEY" -d '{\"subject\":\"Charged twice for March\"}'curl.exe -s -o NUL -w "%{http_code}\n" -X POST "$URL/v1/classify" -H "Content-Type: application/json" -d '{\"subject\":\"x\"}'curl.exe -s -o NUL -w "%{http_code}\n" "$URL/docs"curl.exe -s "$URL/v1/history" -H "X-API-Key: $KEY"La sortie (en ligne) :
{"status":"ready"}{"category":"billing","priority":1,"confidence":0.7,"model_version":"keywords-1.0"}401404{"error":{"code":"history_unavailable","message":"The history is not available. Try again later.","request_id":"a6c8601cf7fb","fields":[]}}Chaque réponse correspond au résultat attendu. La vérification http:// a donné 301, comme dans Configuration opérationnelle : point d’entrée. L’historique donne 503, car la conception infonuagique ne prévoit aucune base de données. Un serveur PostgreSQL géré coûte de l’argent chaque heure, et l’exercice n’en a pas besoin. L’API l’indique dans son format d’erreur standard, avec un identifiant de requête. DATABASE_URL est un paramètre facultatif : avec lui, la même image conserverait un historique.
La collection Postman du cours sur les API fonctionne aussi. Réglez baseUrl sur l’adresse dans l’environnement Deployed (déployé), comme dans Vérifier et nettoyer : exemple détaillé : l’environnement Deployed.
Exemple détaillé : les vérifications simulées
Section intitulée « Exemple détaillé : les vérifications simulées »Les mêmes vérifications, sur http://127.0.0.1:8080. La clé vient du fichier de secrets, donc vous ne la tapez pas :
URL=http://127.0.0.1:8080KEY=$(cat secrets/api_key.txt)curl -s $URL/healthcurl -s $URL/readycurl -s -X POST $URL/v1/classify -H 'Content-Type: application/json' -H "X-API-Key: $KEY" -d @requests/billing.jsoncurl -s -o /dev/null -w "%{http_code}\n" -X POST $URL/v1/classify -H 'Content-Type: application/json' -d '{"subject":"x"}'curl -s -o /dev/null -w "%{http_code}\n" $URL/docscurl -s -o /dev/null -w "%{http_code}\n" $URL/v1/history -H "X-API-Key: $KEY"$URL = "http://127.0.0.1:8080"$KEY = Get-Content secrets/api_key.txt -Rawcurl.exe -s "$URL/health"curl.exe -s "$URL/ready"curl.exe -s -X POST "$URL/v1/classify" -H "Content-Type: application/json" -H "X-API-Key: $KEY" -d "@requests/billing.json"curl.exe -s -o NUL -w "%{http_code}\n" -X POST "$URL/v1/classify" -H "Content-Type: application/json" -d '{\"subject\":\"x\"}'curl.exe -s -o NUL -w "%{http_code}\n" "$URL/docs"curl.exe -s -o NUL -w "%{http_code}\n" "$URL/v1/history" -H "X-API-Key: $KEY"La sortie (simulée) :
{"status":"ok"}{"status":"ready"}{"category":"billing","priority":1,"confidence":0.9,"model_version":"keywords-1.0"}401404503La simulation n’a aucun démarrage à froid (le conteneur fonctionne toujours) ni aucun 301 (il n’y a pas de HTTPS). Notez-le dans les preuves : ces deux vérifications concernent seulement le parcours en ligne.
Lire les journaux
Section intitulée « Lire les journaux »En ligne : les lignes de l’application. L’application écrit les mêmes lignes de journal que sur votre ordinateur. Azure les recueille :
az containerapp logs show -g $RG -n ticket-api --tail 12 --format textLa sortie (en ligne, abrégée) :
2026-10-07T16:17:09.83371 Connecting to the container 'ticket-api'...2026-10-07T16:17:09.93183 Successfully Connected to container: 'ticket-api' [Revision: 'ticket-api--0000001', Replica: 'ticket-api--0000001-84c5f7d4bb-9zszh']2026-10-07T16:16:54.6157582Z stderr F INFO ticket_api: GET /health 200 1ms id=caf6c4a82e302026-10-07T16:16:54.6158236Z stdout F INFO: 127.0.0.1:52216 - "GET /health HTTP/1.1" 200 OK2026-10-07T16:17:04.6139872Z stderr F INFO ticket_api: GET /ready 200 2ms id=81dbb27c35d72026-10-07T16:17:04.6140104Z stdout F INFO: 127.0.0.1:59104 - "GET /ready HTTP/1.1" 200 OK2026-10-07T16:17:05.7192419Z stderr F INFO ticket_api: POST /v1/classify 200 2ms id=95f853debb5b2026-10-07T16:17:05.7193523Z stdout F INFO: 100.100.0.60:49458 - "POST /v1/classify HTTP/1.1" 200 OK2026-10-07T16:17:06.5050443Z stderr F INFO ticket_api: GET / 404 0ms id=71d34007b1a12026-10-07T16:17:06.5051432Z stdout F INFO: 100.100.0.60:49464 - "GET / HTTP/1.1" 404 Not FoundLisez les adresses des clients. 127.0.0.1 correspond aux sondes : elles s’exécutent dans la réplique. 100.100.0.60 est le point d’entrée de la plateforme : chaque requête externe passe par lui. stderr F et stdout F indiquent le flux, et F signifie une ligne complète.
Regardez la dernière requête : GET / avec 404. Personne dans l’équipe ne l’a envoyée. Elle est arrivée quelques minutes après l’attribution de l’adresse publique, suivie d’autres à 16 h 19 et 16 h 27. Ce sont des requêtes que vous n’avez pas envoyées. Elles peuvent venir de programmes qui parcourent Internet à la recherche de nouvelles adresses. Chacune réveille l’application et lance l’attente de 300 secondes avant le retour à zéro instance. Une adresse publique l’est dès la première minute. C’est pourquoi la première révision exigeait déjà la clé.
En ligne : les journaux système. Les événements de la plateforme (récupérations d’images, démarrages, échecs des sondes) sont séparés. Vous les avez utilisés dans Configuration opérationnelle : az containerapp logs show … --type system, sans --format text.
En ligne : Log Analytics. L’espace de travail conserve les lignes, et vous pouvez les chercher avec une requête. Elles sont arrivées environ 2 minutes plus tard :
WS=$(az monitor log-analytics workspace list -g $RG --query "[0].customerId" -o tsv)az monitor log-analytics query -w $WS --analytics-query "ContainerAppConsoleLogs_CL | where Log_s has 'classify' | project TimeGenerated, RevisionName_s, Log_s | take 5" -o table$WS = az monitor log-analytics workspace list -g $RG --query "[0].customerId" -o tsvaz monitor log-analytics query -w $WS --analytics-query "ContainerAppConsoleLogs_CL | where Log_s has 'classify' | project TimeGenerated, RevisionName_s, Log_s | take 5" -o tableLa sortie (en ligne, abrégée) :
Log_s RevisionName_s TimeGenerated------------------------------------------------------------------ ------------------- ---------------------------INFO ticket_api: POST /v1/classify 200 2ms id=95f853debb5b ticket-api--0000001 2026-10-07T16:17:07.067693ZINFO: 100.100.0.60:49458 - "POST /v1/classify HTTP/1.1" 200 OK ticket-api--0000001 2026-10-07T16:17:07.067693Z...Chaque ligne indique sa révision. Après un déploiement, cela vous indique quelle version a répondu à une requête. Pour examiner une erreur, cherchez son identifiant de requête : where Log_s has 'a6c8601cf7fb'.
Simulation : les journaux Compose. Un client envoie une requête sans clé et reçoit un identifiant de requête dans l’erreur :
curl -s -X POST http://127.0.0.1:8080/v1/classify -H 'Content-Type: application/json' -d '{"subject":"Parcel lost"}'docker compose -f deploy/local-cloud.yaml logs api | grep 0008b89dda79La sortie (simulée) :
{"error":{"code":"unauthorized","message":"A valid X-API-Key header is required.","request_id":"0008b89dda79","fields":[]}}api-1 | INFO ticket_api: POST /v1/classify 401 7ms id=0008b89dda79Dans PowerShell, utilisez Select-String 0008b89dda79 au lieu de grep. Les requêtes de la simulation viennent d’une adresse comme 172.21.0.1 (la passerelle du réseau Compose). Ses vérifications de santé viennent de 127.0.0.1, comme les sondes Azure.
Nettoyer à partir d’un inventaire explicite
Section intitulée « Nettoyer à partir d’un inventaire explicite »Un déploiement d’exercice n’est terminé que lorsqu’il a disparu. La règle : d’abord lister, ensuite supprimer, puis vérifier. Seule une liste créée avant la suppression permet de savoir ensuite que rien n’a été oublié.
Description textuelle du diagramme
Listez d’abord toutes les ressources du groupe. Supprimez ensuite le groupe sans attendre la fin de la commande. Demandez plus tard à Azure si le groupe existe encore. Une fois le groupe disparu, cherchez ce que sa suppression laisse : un Key Vault supprimé de façon réversible, à purger. Vérifiez qu’il ne reste aucun groupe ni aucune attribution de rôle. Supprimez ensuite les copies sur votre ordinateur et consignez chaque résultat.
Étape 1 : l’inventaire
Section intitulée « Étape 1 : l’inventaire »az resource list --resource-group $RG -o tableLa sortie (en ligne, avec le Key Vault facultatif du module 4) :
Name ResourceGroup Location Type Status--------------------------------- ---------------------- ------------- ------------------------------------------------ ---------ticketapi4821 rg-ticket-api-practice canadacentral Microsoft.ContainerRegistry/registries Succeededid-ticket-api rg-ticket-api-practice canadacentral Microsoft.ManagedIdentity/userAssignedIdentities Succeededworkspace-rgticketapipracticeXXXX rg-ticket-api-practice canadacentral Microsoft.OperationalInsights/workspaces Succeededcae-ticket-api rg-ticket-api-practice canadacentral Microsoft.App/managedEnvironments Succeededticket-api rg-ticket-api-practice canadacentral Microsoft.App/containerApps Succeededkv-ticket-api-4821 rg-ticket-api-practice canadacentral Microsoft.KeyVault/vaults SucceededCopiez ce tableau dans DEPLOY.md avant toute suppression. Comparez-le à la conception dans docs/cloud-design.md : l’espace de travail est la ressource que personne n’a demandée. Les attributions de rôles ne figurent pas dans cette liste. Elles sont liées aux ressources auxquelles elles s’appliquent.
Étape 2 : supprimer le groupe et vérifier plus tard
Section intitulée « Étape 2 : supprimer le groupe et vérifier plus tard »az group delete --name $RG --yes --no-waitLa commande n’affiche rien et rend la main immédiatement. Sans --no-wait, elle attend la fin de la suppression. Dans le test, cela a pris environ 28 minutes, de 16 h 36 à 17 h 04. L’environnement Container Apps a été la dernière ressource supprimée. Pendant la suppression, az group show -n $RG --query properties.provisioningState -o tsv indique Deleting (suppression en cours). Plus tard, demandez :
az group exists --name $RGtrue (vrai) signifie que la suppression n’est pas terminée : redemandez dans quelques minutes. La sortie à la fin (en ligne) :
falseÉtape 3 : ce que la suppression d’un groupe laisse
Section intitulée « Étape 3 : ce que la suppression d’un groupe laisse »Si vous avez créé le Key Vault, il n’a pas complètement disparu :
az keyvault list-deleted --query "[].{name:name, deleted:properties.deletionDate, purge:properties.scheduledPurgeDate}" -o tableLa sortie (en ligne) :
Name Deleted Purge------------------ ------------------------- -------------------------kv-ticket-api-4821 2026-10-07T16:36:40+00:00 2027-01-05T16:36:40+00:00Le coffre est supprimé de façon réversible. Azure le conserve avec ses secrets pendant 90 jours pour permettre d’annuler une erreur. Cela ne coûte rien. Mais le secret existe encore, et personne ne peut créer un nouveau coffre portant ce nom. Purgez-le :
az keyvault purge --name kv-ticket-api-4821az keyvault list-deleted --query "length(@)"La purge n’affiche rien (code de sortie 0). La sortie du décompte (en ligne) :
0Étape 4 : vérifier qu’il ne reste rien
Section intitulée « Étape 4 : vérifier qu’il ne reste rien »az group list --query "[?contains(name,'ticket-api')].name" -o tsvaz role assignment list --all --query "[?contains(scope,'rg-ticket-api-practice')].{role:roleDefinitionName,scope:scope}" -o tableLes deux commandes n’ont rien affiché (en ligne). Aucun groupe, et les attributions de rôles ont été supprimées avec le registre et le coffre.
Étape 5 : les copies sur votre ordinateur
Section intitulée « Étape 5 : les copies sur votre ordinateur »docker image rm $ACR.azurecr.io/ticket-api:1.1.0 $ACR.azurecr.io/ticket-api:1.1.0-arm64docker logout $ACR.azurecr.ioLa sortie (en ligne) :
Untagged: ticketapi4821.azurecr.io/ticket-api:1.1.0Untagged: ticketapi4821.azurecr.io/ticket-api:1.1.0-arm64...Removing login credentials for ticketapi4821.azurecr.ioOmettez l’étiquette 1.1.0-arm64 si vous ne l’avez pas créée. Ajoutez toute autre étiquette que vous avez envoyée au registre. docker logout supprime le jeton du registre du magasin d’identifiants de Docker. Conservez ticket-api:1.1.0 : c’est votre image, et le travail final l’utilise. Dans le test, les ressources ont existé pendant environ 54 minutes. Le coût prévu : une journée de registre Basic (0,17 $ US) et quelques cents ou moins de calcul, dans l’allocation gratuite.
Le nettoyage de la simulation
Section intitulée « Le nettoyage de la simulation »La même règle : lister, supprimer, vérifier.
docker ps -a --filter name=ticket-api --format "table {{.Names}}\t{{.Image}}\t{{.Status}}"docker volume ls --filter name=ticket-api-registryLa sortie (simulée) :
NAMES IMAGE STATUSticket-api-registry registry:3.1.2 Up 16 secondsticket-api-cloud-sim-api-1 ticket-api:1.1.0 Up 3 minutes (healthy)DRIVER VOLUME NAMElocal ticket-api-registry-dataSi votre base de données du module 3 fonctionne, ses conteneurs figurent aussi dans la liste. Conservez-les. Supprimez ensuite seulement la simulation et le registre :
docker compose -f deploy/local-cloud.yaml downdocker rm -f ticket-api-registrydocker volume rm ticket-api-registry-datadocker image rm 127.0.0.1:5000/ticket-api:1.1.0 127.0.0.1:5000/ticket-api:1.1.0-arm64 registry:3.1.2Ajoutez toute autre étiquette que vous avez envoyée au registre, comme 1.1.0-native.
La sortie (simulée, abrégée) :
Container ticket-api-cloud-sim-api-1 Removed Network ticket-api-cloud-sim_default Removedticket-api-registryticket-api-registry-dataUntagged: 127.0.0.1:5000/ticket-api:1.1.0...Untagged: registry:3.1.2Le registre conserve ses images dans le volume ticket-api-registry-data : 71,24 Mo dans le test. C’est la taille comprimée de l’image amd64 (docker system df -v l’affiche). Sans -v ticket-api-registry-data:… dans la commande docker run, Docker crée un volume avec un nom aléatoire. docker rm -f le laisse alors en place. Un volume nommé rend l’inventaire clair. Vérifiez que les commandes de liste du début n’affichent plus aucune ligne de simulation.
Exercice guidé : vérifier et supprimer
Section intitulée « Exercice guidé : vérifier et supprimer »- Parcours en ligne : réveillez l’application avec
/healthet notez la durée de la première requête. - Exécutez les tests de bon fonctionnement de votre parcours. Comparez chaque réponse au tableau dans Tests de bon fonctionnement.
- Retrouvez votre requête de classification dans les journaux. En ligne :
az containerapp logs show --format text. Simulation :docker compose … logs api | grep <request ID>. - Parcours en ligne : cherchez une requête que vous n’avez pas envoyée, comme
GET /. - Faites l’inventaire :
az resource list -g $RG -o table, ou les listesdocker ps -aetdocker volume ls. Copiez-le dansDEPLOY.md. - Supprimez. En ligne :
az group delete --name $RG --yes --no-wait. Simulation : les quatre commandes du nettoyage de la simulation. - Vérifiez. En ligne :
az group existsjusqu’àfalse(faux), puisaz keyvault list-deleted(purgez au besoin),az group listetaz role assignment list. Simulation : les deux commandes de liste de nouveau. - Supprimez les copies locales et déconnectez-vous du registre (en ligne).
- Consignez chaque vérification et son résultat dans
DEPLOY.md.
Afficher le résultat attendu
Chaque test de bon fonctionnement donne le statut attendu : 200, 200, 200, 401, 404 et (en ligne) 301, avec 503 history_unavailable pour l’historique. az group exists indique false, az keyvault list-deleted --query "length(@)" indique 0, et les listes de groupes et de rôles sont vides. Simulation : il ne reste aucune ligne ticket-api-cloud-sim ou ticket-api-registry. DEPLOY.md contient l’inventaire d’avant la suppression et le résultat de chaque vérification, comme dans le corrigé de Exercices du module.
À vous de jouer : une vérification rapide que vous pouvez répéter
Section intitulée « À vous de jouer : une vérification rapide que vous pouvez répéter »Écrivez smoke-check.sh (ou smoke-check.ps1) pour vérifier /health, /ready, classify avec et sans clé, et /docs. Le script prend l’adresse dans URL et la clé dans TICKET_API_KEY, jamais dans le fichier. Il affiche PASS (réussite) ou FAIL (échec) pour chaque vérification. Il termine avec le code de sortie 1 si une vérification échoue. Que doit-il faire quand TICKET_API_KEY n’est pas définie?
Afficher une réponse pour macOS et Linux
#!/bin/sh# Smoke checks for the deployed ticket API.# Set URL and TICKET_API_KEY first. The key never goes in this file.if [ -z "$URL" ] || [ -z "$TICKET_API_KEY" ]; then echo "Set URL and TICKET_API_KEY first." >&2 exit 2fifail=0check() { name=$1; want=$2; shift 2 got=$(curl -s -o /dev/null -w "%{http_code}" "$@") if [ "$got" = "$want" ]; then echo "PASS $name $got" else echo "FAIL $name: got $got, expected $want" fail=1 fi}check health 200 "$URL/health"check ready 200 "$URL/ready"check classify 200 -X POST "$URL/v1/classify" -H "Content-Type: application/json" \ -H "X-API-Key: $TICKET_API_KEY" -d '{"subject":"Charged twice for March"}'check no-key 401 -X POST "$URL/v1/classify" -H "Content-Type: application/json" -d '{"subject":"x"}'check docs-hidden 404 "$URL/docs"exit $failLa sortie (simulée), d’abord avec la bonne clé, puis avec une mauvaise :
$ TICKET_API_KEY=$(cat secrets/api_key.txt) URL=http://127.0.0.1:8080 sh smoke-check.shPASS health 200PASS ready 200PASS classify 200PASS no-key 401PASS docs-hidden 404$ TICKET_API_KEY=wrong URL=http://127.0.0.1:8080 sh smoke-check.shPASS health 200PASS ready 200FAIL classify: got 401, expected 200PASS no-key 401PASS docs-hidden 404Les codes de sortie étaient 0 et 1. Sans clé, le script s’arrête immédiatement avec le code de sortie 2 et un message clair. Il n’envoie pas de requêtes vouées à l’échec. La fonction de vérification envoie une requête et compare seulement le code d’état. Cela suffit pour une vérification rapide : pytest a vérifié les corps des réponses avant le déploiement. Le même script fonctionne pour les deux parcours. Seule URL change.
Erreur courante
La recherche par étiquette ne trouve rien, mais les ressources sont là
Symptôme (réel) : Amira a ajouté l’étiquette purpose=practice au groupe. Pour trouver toutes les ressources d’exercice, elle fait une recherche par étiquette :
az resource list --tag purpose=practice --query "[].{name:name, type:type}" -o tableLa commande n’affiche rien. Mais az resource list -g rg-ticket-api-practice affiche six ressources. Un nettoyage fondé sur cette recherche indiquerait « rien à supprimer », alors que le registre entraîne des frais chaque jour.
Cause : les étiquettes d’un groupe de ressources ne sont pas transmises aux ressources qu’il contient. Le groupe a l’étiquette. Le registre, l’application et les autres ressources ne l’ont pas. --tag recherche les étiquettes propres aux ressources.
Solution :
- Faites l’inventaire à partir du groupe de ressources :
az resource list -g $RG -o table. Le groupe délimite ce que vous supprimez. Il doit donc aussi délimiter ce que vous listez. - Si vous avez besoin d’étiquettes sur les ressources, ajoutez-les à chacune, par exemple avec
--tagsdans chaque commandecreate(créer). Azure Policy peut aussi imposer ou copier des étiquettes, mais cela dépasse le cadre de ce cours. - Après le nettoyage, vérifiez avec des commandes qui ne peuvent manquer aucune ressource :
az group existsetaz group list.
Pour un vrai service d’IA : les serveurs GPU et le stockage des modèles coûtent beaucoup plus cher que ce registre. Une équipe qui nettoie « tout ce qui porte l’étiquette experiment » peut laisser un serveur GPU fonctionner pendant des semaines. Un inventaire dont vous pouvez prouver l’exhaustivité protège mieux le budget qu’une recherche. Utilisez un groupe par expérience et listez son contenu avant la suppression.
Vérifiez votre compréhension
Pourquoi une vérification rapide inclut-elle des requêtes qui doivent échouer, comme classify sans clé et /docs?
Afficher la réponse
Elles prouvent que les paramètres de sécurité sont appliqués au déploiement. Un mauvais déploiement peut réussir toutes les vérifications des cas normaux. Si le secret ou REQUIRE_API_KEY manque, classify sans clé pourrait répondre 200. L’API serait alors accessible à tout le monde sur Internet. Si SHOW_DOCS est mal configuré, /docs expose toute l’API à des inconnus. Seule une requête qui doit échouer permet de le détecter. Un déploiement qui répond 200 à ces requêtes est défectueux, même si toutes les autres vérifications réussissent.
Votre réponse contient-elle les points principaux de la nôtre ?
Connectez-vous et inscrivez-vous au cours pour enregistrer vos réponses. Chaque vérification compte pour le certificat.
Vérifiez votre compréhension
Dans les journaux réels, certaines requêtes viennent de 127.0.0.1 et d'autres de 100.100.0.60. Lesquelles, et pourquoi un GET / que vous n'avez jamais envoyé compte-t-il?
Afficher la réponse
127.0.0.1 correspond aux sondes : la plateforme les exécute dans la réplique, donc elles proviennent de la réplique elle-même. 100.100.0.60 est le point d’entrée de la plateforme. Toutes les requêtes externes passent par lui, y compris les vôtres. Un GET / que personne dans l’équipe n’a envoyé montre que des inconnus trouvent et appellent une adresse publique en quelques minutes. Des programmes qui scrutent Internet font cela. Cette requête réveille l’application et relance l’attente de 300 secondes avant le retour à zéro réplique. Elle atteindrait aussi une API sans clé. C’est pourquoi la première révision exigeait déjà la clé.
Votre réponse contient-elle les points principaux de la nôtre ?
Connectez-vous et inscrivez-vous au cours pour enregistrer vos réponses. Chaque vérification compte pour le certificat.
Exercices du module
Section intitulée « Exercices du module »Exercice guidé → tâche autonome. Vous avez vu chaque étape avec Amira dans les trois leçons. Refaites maintenant tout le module par vous-même. Faites un déploiement réel si vous avez un abonnement Azure, ou la simulation locale avec les mêmes concepts de configuration. Votre preuve est un fichier, DEPLOY.md, dans le projet. Il comporte trois parties :
- Liste de vérification du déploiement : chaque étape avec la commande exécutée et le résultat observé. Le nom de l’image avec son empreinte et sa plateforme.
- Inventaire des ressources : toutes les ressources, recensées avant la suppression, avec leurs coûts possibles et la façon de les supprimer.
- Registre du nettoyage : chaque suppression et chaque vérification qui suit, avec son résultat.
Indiquez réel ou simulé pour chaque résultat. Ne copiez pas les sorties de la leçon : utilisez les vôtres.
Afficher une réponse modèle (abrégée)
# Deployment record: ticket-api 1.1.0
Route: live (Azure Container Apps, canadacentral).
## Checklist- [x] Image built for linux/amd64 and pushed: ticketapi4821.azurecr.io/ticket-api:1.1.0@sha256:cfd04194eb1b... imagetools inspect: linux/amd64 (+ attestation)- [x] App created: https://ticket-api.nicewater-a307f95a.canadacentral.azurecontainerapps.io- [x] deploy/containerapp.yaml applied: revision ticket-api--0000001, Healthy, traffic 100- [x] Smoke checks (live): /health 200 (18 s cold start), /ready 200, classify 200, no key 401, /docs 404, http 301, history 503 (no database, expected)
## Inventory (before deletion)| Resource | Type | Can it charge? | How to stop it ||---|---|---|---|| ticketapi4821 | Container registry, Basic | Yes, US$0.17 per day | Delete the group || ... | ... | ... | ... |
## Clean-up record (live)| Step | Command | Result ||---|---|---|| Delete the group | az group delete --name rg-ticket-api-practice --yes --no-wait | started 16:36 || Group gone | az group exists --name rg-ticket-api-practice | false (17:04) || ... | ... | ... |L’exemple complet, avec la variante simulée, est le fichier DEPLOY.md de l’instantané end-of-m05. La personne qui le révise l’évalue comme démontré quand trois conditions sont remplies. Les vérifications rapides incluent les cas d’échec. L’inventaire comprend l’espace de travail automatique. Chaque ligne de nettoyage comprend une vérification avec son résultat, pas seulement la commande de suppression.
Vérification du module
Section intitulée « Vérification du module »Vérifiez votre compréhension
Rappel : nommez les trois types de sondes d'Azure Container Apps, le point de terminaison utilisé par l'API de billets pour chacun, et l'action de la plateforme en cas d'échec.
Afficher la réponse
Démarrage sur /health : la plateforme attend jusqu’à 30 échecs, soit 60 secondes, puis redémarre la copie. Les autres sondes attendent celle-ci. Vitalité sur /health : après 3 échecs, la plateforme redémarre la copie. Disponibilité sur /ready : après 3 échecs, la plateforme n’envoie plus de requêtes à la copie, mais ne la redémarre pas. Une nouvelle révision reçoit le trafic seulement quand elle est prête.
Votre réponse contient-elle les points principaux de la nôtre ?
Connectez-vous et inscrivez-vous au cours pour enregistrer vos réponses. Chaque vérification compte pour le certificat.
Vérifiez votre compréhension
Prédiction : vous exécutez az group delete --name rg-ticket-api-practice --yes et la commande se termine sans erreur. Quels coûts et risques peuvent subsister, et comment les vérifier?
Afficher la réponse
Si la commande est terminée, les ressources du groupe ont disparu. Le registre, l’application et l’espace de travail n’entraînent donc plus de frais. Mais un Key Vault en suppression réversible reste présent pendant 90 jours. Il ne coûte rien, mais ses secrets existent encore et son nom reste réservé. Vérifiez avec az keyvault list-deleted et purgez-le. Les ressources des autres groupes n’ont jamais figuré dans l’inventaire. Vérifiez donc az group list pour repérer un groupe d’exercice oublié. Sur votre ordinateur, le jeton du registre et les étiquettes envoyées restent présents jusqu’à docker logout et docker image rm. Avec --no-wait, la commande se termine immédiatement. Vérifiez donc az group exists jusqu’à ce qu’elle indique false (faux).
Votre réponse contient-elle les points principaux de la nôtre ?
Connectez-vous et inscrivez-vous au cours pour enregistrer vos réponses. Chaque vérification compte pour le certificat.
Vérifiez votre compréhension
Vérification finale : montrez votre DEPLOY.md. Documente-t-il un accès fonctionnel, l'inventaire des ressources et des étapes de nettoyage vérifiées?
Afficher la réponse
Un fichier DEPLOY.md complet contient : l’adresse (réelle) ou http://127.0.0.1:8080 (simulée), et l’image avec son empreinte et sa plateforme; les vérifications rapides avec leurs résultats, dont 401 sans clé et 404 pour /docs; l’inventaire fait avant la suppression, avec l’espace de travail Log Analytics automatique (réel) ou le volume du registre (simulé), et les coûts possibles de chaque élément; pour chaque suppression, une vérification avec son résultat : az group exists → false, az keyvault list-deleted → 0, listes de groupes et de rôles vides, docker logout (réel), ou listes docker ps -a et docker volume ls vides (simulé). Chaque résultat porte la mention réel ou simulé. Si une vérification manque, le nettoyage est présumé, pas vérifié.
Votre réponse contient-elle les points principaux de la nôtre ?
Connectez-vous et inscrivez-vous au cours pour enregistrer vos réponses. Chaque vérification compte pour le certificat.
Récapitulatif
- Une vérification rapide valide ce déploiement et inclut les requêtes qui doivent échouer : sans clé →
401,/docs→404. - Réveillez une application à zéro réplique avant une vérification chronométrée : la première requête a entraîné un démarrage à froid de 18 secondes.
- Les journaux de l’application montrent vos requêtes, les sondes (
127.0.0.1) et les requêtes que vous n’avez pas envoyées. Recherchez par identifiant de requête. - Nettoyez à partir d’un inventaire explicite : listez le groupe, supprimez-le avec
--no-wait, vérifiezaz group exists, purgez un Key Vault en suppression réversible, vérifiez les groupes et les rôles, supprimez les copies locales. Consignez chaque résultat dansDEPLOY.md. - Les étiquettes d’un groupe ne sont pas transmises à ses ressources. Faites l’inventaire à partir du groupe.
Réinitialisation. Pour recommencer ce module : en mode réel, le groupe de ressources doit avoir disparu (az group exists → false). En mode simulé, exécutez le nettoyage simulé ci-dessus. Votre projet et ticket-api:1.1.0 restent en place. Pour réinitialiser les fichiers du projet, copiez l’instantané end-of-m04. Pour voir ce que Docker stocke encore, exécutez docker system df.
Le cours. Vous avez commencé par « ça fonctionne sur mon ordinateur ». Maintenant, vous pouvez intégrer une API dans une image, avec son système d’exploitation, Python et ses paquets. Vous pouvez la construire rapidement et sans secrets, puis l’exécuter avec ses paramètres et une base de données. Vous pouvez l’associer aux services de base du nuage et à leurs coûts. Vous pouvez la déployer avec des vérifications et des limites, puis la supprimer avec des preuves. Le travail final vous demande de recommencer, par vous-même, dans un nouvel environnement.
Leçon suivante : Travail final